Merge remote-tracking branch 'upstream_linaro/linaro-libnl2_0'

* upstream_linaro/linaro-libnl2_0: (159 commits)
  Updating group definition to follow 3.13 kernel
  Adding version.h
  Adding support for netlink API and quota in nfaccounting
  Compile 2.0 with Kitkat
  Don't include pktloc_syntax.h in BUILT_SOURCES
  route_obj: don't add empty destination to nlmsg
  automake: add ${top_builddir}/include to AM_CFLAGS
  Use CPPFLAGS
  Put preprocessor definitions in AM_CPPFLAGS
  add user data to change_func_t for caches
  Trivial fix for TBF memleak
  libnl: optionally disable cli tools.
  Ignore vim swap files
  Let git ignore generated pktloc source files
  Fix compile warning in utils.c
  Fix compile warning in nl.c
  Packet Location Interface
  src/nf-queue.c: cleanup and improve performance of test program for NF_QUEUE
  Don't install private header files.
  object: fix attribute comparison
  ...

Conflicts:
	Android.mk
	include/linux/netfilter/nfnetlink.h

Change-Id: I383749ca16113b2ae8cfc7729aee8bbc8a36dc9e
diff --git a/.gitignore b/.gitignore
index ce140c3..17a67a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,28 @@
-*.[od]
-Makefile.opts
-config.log
-config.status
-autom4te.cache
-libnl-1.pc
-configure
-doc/Doxyfile
-lib/defs.h
+.deps
+.libs
+.dirstamp
+*.in
+*.la
+*.lo
+*.o
+*.swp
+Makefile
+/lib/stamp-h1
+
+/libnl-1.pc
+/doc/Doxyfile
+/lib/defs.h
+cscope.*
+
+/aclocal.m4
+/autom4te.cache
+/compile
+/config.*
+/configure
+/depcomp
+/libtool
+/ltmain.sh
+/install-sh
+/missing
+
+/*.pc
diff --git a/Android.mk b/Android.mk
index 4d61c47..b01e375 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,15 +23,12 @@
 	lib/genl/genl.c \
 	lib/route/rtnl.c \
 	lib/route/route_utils.c \
-	lib/netfilter/nfnl.c
+	lib/netfilter/nfnl.c \
+	lib/error.c
 
-#LOCAL_CFLAGS :=
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-	#kernel/android-2.6.32/include
 
 LOCAL_MODULE_TAGS := eng
 LOCAL_MODULE := libnl
 
 include $(BUILD_SHARED_LIBRARY)
-#include $(BUILD_STATIC_LIBRARY)
-
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 826b9a3..0000000
--- a/Makefile
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# Makefile
-#
-# 	This library is free software; you can redistribute it and/or
-#	modify it under the terms of the GNU Lesser General Public
-#	License as published by the Free Software Foundation version 2.1
-#	of the License.
-#
-# Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
-#
-
-ifeq ($(shell [ ! -r Makefile.opts ] && echo 1),)
-    include Makefile.opts
-endif
-
-SUBDIRS := lib include doc src tests
-.PHONY: all clean distclean install gendoc $(SUBDIRS)
-
-all: Makefile.opts
-	@for dir in $(SUBDIRS); do \
-		echo "Entering $$dir" && $(MAKE) -C $$dir || exit $$?; \
-	done
-
-clean: Makefile.opts
-	rm -f cscope.*
-	@for dir in $(SUBDIRS); do \
-		echo "Entering $$dir" && $(MAKE) -C $$dir clean || exit $$?; \
-	done
-
-distclean: clean
-	@$(RM) -rf Makefile.opts autom4te.cache config.log config.status
-	@for dir in $(SUBDIRS); do \
-		echo "Entering $$dir" && $(MAKE) -C $$dir distclean || exit $$?; \
-	done
-
-install: Makefile.opts
-	@for dir in $(SUBDIRS); do \
-		echo "Entering $$dir" && cd $$dir && $(MAKE) install && cd ..; \
-	done
-	mkdir -p $(DESTDIR)$(libdir)/pkgconfig/
-	install -m 0644 libnl-1.pc $(DESTDIR)$(libdir)/pkgconfig/
-
-gendoc:
-	$(MAKE) -C doc gendoc
-
-show: Makefile.opts
-	@echo "CC:          $(CC)"
-	@echo "RM:          $(RM)"
-	@echo "CFLAGS:      $(CFLAGS)"
-	@echo "DEPFLAGS:    $(DEPFLAGS)"
-	@echo "LDFLAGS:     $(LDFLAGS)"
-	@echo "DESTDIR:     $(DESTDIR)"
-	@echo "prefix:      $(prefix)"
-	@echo "libdir:      $(libdir)"
-	@echo "includedir:  $(includedir)"
-
-cscope:
-	cscope -b -q -R -Iinclude -slib -ssrc
-
-
-$(SUBDIRS):
-	cd $@ && $(MAKE)
-
--include Makefile.rules
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..8f9438c
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,20 @@
+# -*- Makefile -*-
+
+ACLOCAL_AMFLAGS = -I m4
+
+OPT_DIRS =
+
+if ENABLE_CLI
+OPT_DIRS += src
+endif
+
+SUBDIRS = include lib doc $(OPT_DIRS)
+
+pkgconfig_DATA = libnl-2.0.pc
+
+sysconfdir = @sysconfdir@/libnl
+sysconf_DATA = etc/pktloc
+
+.PHONY: cscope
+cscope:
+	cscope -b -q -R -Iinclude -slib -ssrc;
diff --git a/Makefile.opts.in b/Makefile.opts.in
deleted file mode 100644
index 87d229b..0000000
--- a/Makefile.opts.in
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Makefile.opts.in
-#
-# 	This library is free software; you can redistribute it and/or
-#	modify it under the terms of the GNU Lesser General Public
-#	License as published by the Free Software Foundation version 2.1
-#	of the License.
-#
-# Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
-#
-
-CC               := @CC@
-CFLAGS           := @CFLAGS@
-LDFLAGS          := @LDFLAGS@
-CPPFLAGS         := @CPPFLAGS@
-PACKAGE_NAME     := @PACKAGE_NAME@
-PACKAGE_VERSION  := @PACKAGE_VERSION@
-
-LIBNL_LIB        := @LIBNL_LIB@
-
-prefix           := @prefix@
-exec_prefix      := @exec_prefix@
-libdir           := @libdir@
-includedir       := @includedir@
-mandir           := @mandir@
-sysconfdir       := @sysconfdir@
-
-AR               := ar
-RM               := rm
-LN               := ln
-
-DEPFLAGS         += -M -I../include/ -I. $(CPPFLAGS)
-CFLAGS           += -g -I./include -I../include -I. $(CPPFLAGS) -D_GNU_SOURCE
-MAKEFLAGS        += --no-print-directory
-
-ifeq ($(CC),gcc)
-CFLAGS           += -Wall -ggdb
-endif
-
diff --git a/Makefile.rules b/Makefile.rules
deleted file mode 100644
index 5a1decf..0000000
--- a/Makefile.rules
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Makefile.rules
-#
-# 	This library is free software; you can redistribute it and/or
-#	modify it under the terms of the GNU Lesser General Public
-#	License as published by the Free Software Foundation version 2.1
-#	of the License.
-#
-# Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
-#
-
-.SUFFIXES:
-.SUFFIXES: .d .c
-
-%.o: %.c
-	@echo "  CC $<"; \
-	$(CC) $(CFLAGS) -c -o $@ $<
-
-%.d: %.c
-	@echo "  DEP $<"; \
-	$(CC) $(DEPFLAGS) $< > $@.tmp; \
-	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.tmp > $@; \
-	rm -f $@.tmp
-
-Makefile.opts:
-	@echo "***"
-	@echo "*** No configuration found, please run ./configure"
-	@echo "***"
-	@exit 1
-
-ifneq ($(MAKECMDGOALS),clean)
-ifneq ($(MAKECMDGOALS),distclean)
-ifneq ($(DEPS),)
--include $(DEPS)
-endif
-endif
-endif
diff --git a/aclocal.m4 b/aclocal.m4
deleted file mode 100644
index 177c013..0000000
--- a/aclocal.m4
+++ /dev/null
@@ -1,831 +0,0 @@
-dnl aclocal.m4 generated automatically by aclocal 1.4-p6
-
-dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
-dnl This file is free software; the Free Software Foundation
-dnl gives unlimited permission to copy and/or distribute it,
-dnl with or without modifications, as long as this notice is preserved.
-
-dnl This program is distributed in the hope that it will be useful,
-dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-dnl PARTICULAR PURPOSE.
-
-# lib-prefix.m4 serial 4 (gettext-0.14.2)
-dnl Copyright (C) 2001-2005 Free Software Foundation, Inc.
-dnl This file is free software; the Free Software Foundation
-dnl gives unlimited permission to copy and/or distribute it,
-dnl with or without modifications, as long as this notice is preserved.
-
-dnl From Bruno Haible.
-
-dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and
-dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't
-dnl require excessive bracketing.
-ifdef([AC_HELP_STRING],
-[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])],
-[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])])
-
-dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed
-dnl to access previously installed libraries. The basic assumption is that
-dnl a user will want packages to use other packages he previously installed
-dnl with the same --prefix option.
-dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate
-dnl libraries, but is otherwise very convenient.
-AC_DEFUN([AC_LIB_PREFIX],
-[
-  AC_BEFORE([$0], [AC_LIB_LINKFLAGS])
-  AC_REQUIRE([AC_PROG_CC])
-  AC_REQUIRE([AC_CANONICAL_HOST])
-  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
-  dnl By default, look in $includedir and $libdir.
-  use_additional=yes
-  AC_LIB_WITH_FINAL_PREFIX([
-    eval additional_includedir=\"$includedir\"
-    eval additional_libdir=\"$libdir\"
-  ])
-  AC_LIB_ARG_WITH([lib-prefix],
-[  --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib
-  --without-lib-prefix    don't search for libraries in includedir and libdir],
-[
-    if test "X$withval" = "Xno"; then
-      use_additional=no
-    else
-      if test "X$withval" = "X"; then
-        AC_LIB_WITH_FINAL_PREFIX([
-          eval additional_includedir=\"$includedir\"
-          eval additional_libdir=\"$libdir\"
-        ])
-      else
-        additional_includedir="$withval/include"
-        additional_libdir="$withval/lib"
-      fi
-    fi
-])
-  if test $use_additional = yes; then
-    dnl Potentially add $additional_includedir to $CPPFLAGS.
-    dnl But don't add it
-    dnl   1. if it's the standard /usr/include,
-    dnl   2. if it's already present in $CPPFLAGS,
-    dnl   3. if it's /usr/local/include and we are using GCC on Linux,
-    dnl   4. if it doesn't exist as a directory.
-    if test "X$additional_includedir" != "X/usr/include"; then
-      haveit=
-      for x in $CPPFLAGS; do
-        AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
-        if test "X$x" = "X-I$additional_includedir"; then
-          haveit=yes
-          break
-        fi
-      done
-      if test -z "$haveit"; then
-        if test "X$additional_includedir" = "X/usr/local/include"; then
-          if test -n "$GCC"; then
-            case $host_os in
-              linux* | gnu* | k*bsd*-gnu) haveit=yes;;
-            esac
-          fi
-        fi
-        if test -z "$haveit"; then
-          if test -d "$additional_includedir"; then
-            dnl Really add $additional_includedir to $CPPFLAGS.
-            CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir"
-          fi
-        fi
-      fi
-    fi
-    dnl Potentially add $additional_libdir to $LDFLAGS.
-    dnl But don't add it
-    dnl   1. if it's the standard /usr/lib,
-    dnl   2. if it's already present in $LDFLAGS,
-    dnl   3. if it's /usr/local/lib and we are using GCC on Linux,
-    dnl   4. if it doesn't exist as a directory.
-    if test "X$additional_libdir" != "X/usr/lib"; then
-      haveit=
-      for x in $LDFLAGS; do
-        AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
-        if test "X$x" = "X-L$additional_libdir"; then
-          haveit=yes
-          break
-        fi
-      done
-      if test -z "$haveit"; then
-        if test "X$additional_libdir" = "X/usr/local/lib"; then
-          if test -n "$GCC"; then
-            case $host_os in
-              linux*) haveit=yes;;
-            esac
-          fi
-        fi
-        if test -z "$haveit"; then
-          if test -d "$additional_libdir"; then
-            dnl Really add $additional_libdir to $LDFLAGS.
-            LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir"
-          fi
-        fi
-      fi
-    fi
-  fi
-])
-
-dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix,
-dnl acl_final_exec_prefix, containing the values to which $prefix and
-dnl $exec_prefix will expand at the end of the configure script.
-AC_DEFUN([AC_LIB_PREPARE_PREFIX],
-[
-  dnl Unfortunately, prefix and exec_prefix get only finally determined
-  dnl at the end of configure.
-  if test "X$prefix" = "XNONE"; then
-    acl_final_prefix="$ac_default_prefix"
-  else
-    acl_final_prefix="$prefix"
-  fi
-  if test "X$exec_prefix" = "XNONE"; then
-    acl_final_exec_prefix='${prefix}'
-  else
-    acl_final_exec_prefix="$exec_prefix"
-  fi
-  acl_save_prefix="$prefix"
-  prefix="$acl_final_prefix"
-  eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
-  prefix="$acl_save_prefix"
-])
-
-dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the
-dnl variables prefix and exec_prefix bound to the values they will have
-dnl at the end of the configure script.
-AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX],
-[
-  acl_save_prefix="$prefix"
-  prefix="$acl_final_prefix"
-  acl_save_exec_prefix="$exec_prefix"
-  exec_prefix="$acl_final_exec_prefix"
-  $1
-  exec_prefix="$acl_save_exec_prefix"
-  prefix="$acl_save_prefix"
-])
-
-# lib-link.m4 serial 6 (gettext-0.14.3)
-dnl Copyright (C) 2001-2005 Free Software Foundation, Inc.
-dnl This file is free software; the Free Software Foundation
-dnl gives unlimited permission to copy and/or distribute it,
-dnl with or without modifications, as long as this notice is preserved.
-
-dnl From Bruno Haible.
-
-AC_PREREQ(2.50)
-
-dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and
-dnl the libraries corresponding to explicit and implicit dependencies.
-dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and
-dnl augments the CPPFLAGS variable.
-AC_DEFUN([AC_LIB_LINKFLAGS],
-[
-  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
-  AC_REQUIRE([AC_LIB_RPATH])
-  define([Name],[translit([$1],[./-], [___])])
-  define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
-                               [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
-  AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [
-    AC_LIB_LINKFLAGS_BODY([$1], [$2])
-    ac_cv_lib[]Name[]_libs="$LIB[]NAME"
-    ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME"
-    ac_cv_lib[]Name[]_cppflags="$INC[]NAME"
-  ])
-  LIB[]NAME="$ac_cv_lib[]Name[]_libs"
-  LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs"
-  INC[]NAME="$ac_cv_lib[]Name[]_cppflags"
-  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
-  AC_SUBST([LIB]NAME)
-  AC_SUBST([LTLIB]NAME)
-  dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the
-  dnl results of this search when this library appears as a dependency.
-  HAVE_LIB[]NAME=yes
-  undefine([Name])
-  undefine([NAME])
-])
-
-dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode)
-dnl searches for libname and the libraries corresponding to explicit and
-dnl implicit dependencies, together with the specified include files and
-dnl the ability to compile and link the specified testcode. If found, it
-dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and
-dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and
-dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs
-dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty.
-AC_DEFUN([AC_LIB_HAVE_LINKFLAGS],
-[
-  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
-  AC_REQUIRE([AC_LIB_RPATH])
-  define([Name],[translit([$1],[./-], [___])])
-  define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
-                               [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
-
-  dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME
-  dnl accordingly.
-  AC_LIB_LINKFLAGS_BODY([$1], [$2])
-
-  dnl Add $INC[]NAME to CPPFLAGS before performing the following checks,
-  dnl because if the user has installed lib[]Name and not disabled its use
-  dnl via --without-lib[]Name-prefix, he wants to use it.
-  ac_save_CPPFLAGS="$CPPFLAGS"
-  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
-
-  AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [
-    ac_save_LIBS="$LIBS"
-    LIBS="$LIBS $LIB[]NAME"
-    AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no])
-    LIBS="$ac_save_LIBS"
-  ])
-  if test "$ac_cv_lib[]Name" = yes; then
-    HAVE_LIB[]NAME=yes
-    AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.])
-    AC_MSG_CHECKING([how to link with lib[]$1])
-    AC_MSG_RESULT([$LIB[]NAME])
-  else
-    HAVE_LIB[]NAME=no
-    dnl If $LIB[]NAME didn't lead to a usable library, we don't need
-    dnl $INC[]NAME either.
-    CPPFLAGS="$ac_save_CPPFLAGS"
-    LIB[]NAME=
-    LTLIB[]NAME=
-  fi
-  AC_SUBST([HAVE_LIB]NAME)
-  AC_SUBST([LIB]NAME)
-  AC_SUBST([LTLIB]NAME)
-  undefine([Name])
-  undefine([NAME])
-])
-
-dnl Determine the platform dependent parameters needed to use rpath:
-dnl libext, shlibext, hardcode_libdir_flag_spec, hardcode_libdir_separator,
-dnl hardcode_direct, hardcode_minus_L.
-AC_DEFUN([AC_LIB_RPATH],
-[
-  dnl Tell automake >= 1.10 to complain if config.rpath is missing.
-  m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])])
-  AC_REQUIRE([AC_PROG_CC])                dnl we use $CC, $GCC, $LDFLAGS
-  AC_REQUIRE([AC_LIB_PROG_LD])            dnl we use $LD, $with_gnu_ld
-  AC_REQUIRE([AC_CANONICAL_HOST])         dnl we use $host
-  AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir
-  AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [
-    CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
-    ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
-    . ./conftest.sh
-    rm -f ./conftest.sh
-    acl_cv_rpath=done
-  ])
-  wl="$acl_cv_wl"
-  libext="$acl_cv_libext"
-  shlibext="$acl_cv_shlibext"
-  hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
-  hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
-  hardcode_direct="$acl_cv_hardcode_direct"
-  hardcode_minus_L="$acl_cv_hardcode_minus_L"
-  dnl Determine whether the user wants rpath handling at all.
-  AC_ARG_ENABLE(rpath,
-    [  --disable-rpath         do not hardcode runtime library paths],
-    :, enable_rpath=yes)
-])
-
-dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and
-dnl the libraries corresponding to explicit and implicit dependencies.
-dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables.
-AC_DEFUN([AC_LIB_LINKFLAGS_BODY],
-[
-  define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
-                               [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
-  dnl By default, look in $includedir and $libdir.
-  use_additional=yes
-  AC_LIB_WITH_FINAL_PREFIX([
-    eval additional_includedir=\"$includedir\"
-    eval additional_libdir=\"$libdir\"
-  ])
-  AC_LIB_ARG_WITH([lib$1-prefix],
-[  --with-lib$1-prefix[=DIR]  search for lib$1 in DIR/include and DIR/lib
-  --without-lib$1-prefix     don't search for lib$1 in includedir and libdir],
-[
-    if test "X$withval" = "Xno"; then
-      use_additional=no
-    else
-      if test "X$withval" = "X"; then
-        AC_LIB_WITH_FINAL_PREFIX([
-          eval additional_includedir=\"$includedir\"
-          eval additional_libdir=\"$libdir\"
-        ])
-      else
-        additional_includedir="$withval/include"
-        additional_libdir="$withval/lib"
-      fi
-    fi
-])
-  dnl Search the library and its dependencies in $additional_libdir and
-  dnl $LDFLAGS. Using breadth-first-seach.
-  LIB[]NAME=
-  LTLIB[]NAME=
-  INC[]NAME=
-  rpathdirs=
-  ltrpathdirs=
-  names_already_handled=
-  names_next_round='$1 $2'
-  while test -n "$names_next_round"; do
-    names_this_round="$names_next_round"
-    names_next_round=
-    for name in $names_this_round; do
-      already_handled=
-      for n in $names_already_handled; do
-        if test "$n" = "$name"; then
-          already_handled=yes
-          break
-        fi
-      done
-      if test -z "$already_handled"; then
-        names_already_handled="$names_already_handled $name"
-        dnl See if it was already located by an earlier AC_LIB_LINKFLAGS
-        dnl or AC_LIB_HAVE_LINKFLAGS call.
-        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
-        eval value=\"\$HAVE_LIB$uppername\"
-        if test -n "$value"; then
-          if test "$value" = yes; then
-            eval value=\"\$LIB$uppername\"
-            test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value"
-            eval value=\"\$LTLIB$uppername\"
-            test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value"
-          else
-            dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined
-            dnl that this library doesn't exist. So just drop it.
-            :
-          fi
-        else
-          dnl Search the library lib$name in $additional_libdir and $LDFLAGS
-          dnl and the already constructed $LIBNAME/$LTLIBNAME.
-          found_dir=
-          found_la=
-          found_so=
-          found_a=
-          if test $use_additional = yes; then
-            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then
-              found_dir="$additional_libdir"
-              found_so="$additional_libdir/lib$name.$shlibext"
-              if test -f "$additional_libdir/lib$name.la"; then
-                found_la="$additional_libdir/lib$name.la"
-              fi
-            else
-              if test -f "$additional_libdir/lib$name.$libext"; then
-                found_dir="$additional_libdir"
-                found_a="$additional_libdir/lib$name.$libext"
-                if test -f "$additional_libdir/lib$name.la"; then
-                  found_la="$additional_libdir/lib$name.la"
-                fi
-              fi
-            fi
-          fi
-          if test "X$found_dir" = "X"; then
-            for x in $LDFLAGS $LTLIB[]NAME; do
-              AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
-              case "$x" in
-                -L*)
-                  dir=`echo "X$x" | sed -e 's/^X-L//'`
-                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then
-                    found_dir="$dir"
-                    found_so="$dir/lib$name.$shlibext"
-                    if test -f "$dir/lib$name.la"; then
-                      found_la="$dir/lib$name.la"
-                    fi
-                  else
-                    if test -f "$dir/lib$name.$libext"; then
-                      found_dir="$dir"
-                      found_a="$dir/lib$name.$libext"
-                      if test -f "$dir/lib$name.la"; then
-                        found_la="$dir/lib$name.la"
-                      fi
-                    fi
-                  fi
-                  ;;
-              esac
-              if test "X$found_dir" != "X"; then
-                break
-              fi
-            done
-          fi
-          if test "X$found_dir" != "X"; then
-            dnl Found the library.
-            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name"
-            if test "X$found_so" != "X"; then
-              dnl Linking with a shared library. We attempt to hardcode its
-              dnl directory into the executable's runpath, unless it's the
-              dnl standard /usr/lib.
-              if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
-                dnl No hardcoding is needed.
-                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
-              else
-                dnl Use an explicit option to hardcode DIR into the resulting
-                dnl binary.
-                dnl Potentially add DIR to ltrpathdirs.
-                dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
-                haveit=
-                for x in $ltrpathdirs; do
-                  if test "X$x" = "X$found_dir"; then
-                    haveit=yes
-                    break
-                  fi
-                done
-                if test -z "$haveit"; then
-                  ltrpathdirs="$ltrpathdirs $found_dir"
-                fi
-                dnl The hardcoding into $LIBNAME is system dependent.
-                if test "$hardcode_direct" = yes; then
-                  dnl Using DIR/libNAME.so during linking hardcodes DIR into the
-                  dnl resulting binary.
-                  LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
-                else
-                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
-                    dnl Use an explicit option to hardcode DIR into the resulting
-                    dnl binary.
-                    LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
-                    dnl Potentially add DIR to rpathdirs.
-                    dnl The rpathdirs will be appended to $LIBNAME at the end.
-                    haveit=
-                    for x in $rpathdirs; do
-                      if test "X$x" = "X$found_dir"; then
-                        haveit=yes
-                        break
-                      fi
-                    done
-                    if test -z "$haveit"; then
-                      rpathdirs="$rpathdirs $found_dir"
-                    fi
-                  else
-                    dnl Rely on "-L$found_dir".
-                    dnl But don't add it if it's already contained in the LDFLAGS
-                    dnl or the already constructed $LIBNAME
-                    haveit=
-                    for x in $LDFLAGS $LIB[]NAME; do
-                      AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
-                      if test "X$x" = "X-L$found_dir"; then
-                        haveit=yes
-                        break
-                      fi
-                    done
-                    if test -z "$haveit"; then
-                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir"
-                    fi
-                    if test "$hardcode_minus_L" != no; then
-                      dnl FIXME: Not sure whether we should use
-                      dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
-                      dnl here.
-                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
-                    else
-                      dnl We cannot use $hardcode_runpath_var and LD_RUN_PATH
-                      dnl here, because this doesn't fit in flags passed to the
-                      dnl compiler. So give up. No hardcoding. This affects only
-                      dnl very old systems.
-                      dnl FIXME: Not sure whether we should use
-                      dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
-                      dnl here.
-                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
-                    fi
-                  fi
-                fi
-              fi
-            else
-              if test "X$found_a" != "X"; then
-                dnl Linking with a static library.
-                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a"
-              else
-                dnl We shouldn't come here, but anyway it's good to have a
-                dnl fallback.
-                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name"
-              fi
-            fi
-            dnl Assume the include files are nearby.
-            additional_includedir=
-            case "$found_dir" in
-              */lib | */lib/)
-                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
-                additional_includedir="$basedir/include"
-                ;;
-            esac
-            if test "X$additional_includedir" != "X"; then
-              dnl Potentially add $additional_includedir to $INCNAME.
-              dnl But don't add it
-              dnl   1. if it's the standard /usr/include,
-              dnl   2. if it's /usr/local/include and we are using GCC on Linux,
-              dnl   3. if it's already present in $CPPFLAGS or the already
-              dnl      constructed $INCNAME,
-              dnl   4. if it doesn't exist as a directory.
-              if test "X$additional_includedir" != "X/usr/include"; then
-                haveit=
-                if test "X$additional_includedir" = "X/usr/local/include"; then
-                  if test -n "$GCC"; then
-                    case $host_os in
-                      linux* | gnu* | k*bsd*-gnu) haveit=yes;;
-                    esac
-                  fi
-                fi
-                if test -z "$haveit"; then
-                  for x in $CPPFLAGS $INC[]NAME; do
-                    AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
-                    if test "X$x" = "X-I$additional_includedir"; then
-                      haveit=yes
-                      break
-                    fi
-                  done
-                  if test -z "$haveit"; then
-                    if test -d "$additional_includedir"; then
-                      dnl Really add $additional_includedir to $INCNAME.
-                      INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir"
-                    fi
-                  fi
-                fi
-              fi
-            fi
-            dnl Look for dependencies.
-            if test -n "$found_la"; then
-              dnl Read the .la file. It defines the variables
-              dnl dlname, library_names, old_library, dependency_libs, current,
-              dnl age, revision, installed, dlopen, dlpreopen, libdir.
-              save_libdir="$libdir"
-              case "$found_la" in
-                */* | *\\*) . "$found_la" ;;
-                *) . "./$found_la" ;;
-              esac
-              libdir="$save_libdir"
-              dnl We use only dependency_libs.
-              for dep in $dependency_libs; do
-                case "$dep" in
-                  -L*)
-                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
-                    dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME.
-                    dnl But don't add it
-                    dnl   1. if it's the standard /usr/lib,
-                    dnl   2. if it's /usr/local/lib and we are using GCC on Linux,
-                    dnl   3. if it's already present in $LDFLAGS or the already
-                    dnl      constructed $LIBNAME,
-                    dnl   4. if it doesn't exist as a directory.
-                    if test "X$additional_libdir" != "X/usr/lib"; then
-                      haveit=
-                      if test "X$additional_libdir" = "X/usr/local/lib"; then
-                        if test -n "$GCC"; then
-                          case $host_os in
-                            linux* | gnu* | k*bsd*-gnu) haveit=yes;;
-                          esac
-                        fi
-                      fi
-                      if test -z "$haveit"; then
-                        haveit=
-                        for x in $LDFLAGS $LIB[]NAME; do
-                          AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
-                          if test "X$x" = "X-L$additional_libdir"; then
-                            haveit=yes
-                            break
-                          fi
-                        done
-                        if test -z "$haveit"; then
-                          if test -d "$additional_libdir"; then
-                            dnl Really add $additional_libdir to $LIBNAME.
-                            LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir"
-                          fi
-                        fi
-                        haveit=
-                        for x in $LDFLAGS $LTLIB[]NAME; do
-                          AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
-                          if test "X$x" = "X-L$additional_libdir"; then
-                            haveit=yes
-                            break
-                          fi
-                        done
-                        if test -z "$haveit"; then
-                          if test -d "$additional_libdir"; then
-                            dnl Really add $additional_libdir to $LTLIBNAME.
-                            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir"
-                          fi
-                        fi
-                      fi
-                    fi
-                    ;;
-                  -R*)
-                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
-                    if test "$enable_rpath" != no; then
-                      dnl Potentially add DIR to rpathdirs.
-                      dnl The rpathdirs will be appended to $LIBNAME at the end.
-                      haveit=
-                      for x in $rpathdirs; do
-                        if test "X$x" = "X$dir"; then
-                          haveit=yes
-                          break
-                        fi
-                      done
-                      if test -z "$haveit"; then
-                        rpathdirs="$rpathdirs $dir"
-                      fi
-                      dnl Potentially add DIR to ltrpathdirs.
-                      dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
-                      haveit=
-                      for x in $ltrpathdirs; do
-                        if test "X$x" = "X$dir"; then
-                          haveit=yes
-                          break
-                        fi
-                      done
-                      if test -z "$haveit"; then
-                        ltrpathdirs="$ltrpathdirs $dir"
-                      fi
-                    fi
-                    ;;
-                  -l*)
-                    dnl Handle this in the next round.
-                    names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
-                    ;;
-                  *.la)
-                    dnl Handle this in the next round. Throw away the .la's
-                    dnl directory; it is already contained in a preceding -L
-                    dnl option.
-                    names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
-                    ;;
-                  *)
-                    dnl Most likely an immediate library name.
-                    LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep"
-                    LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep"
-                    ;;
-                esac
-              done
-            fi
-          else
-            dnl Didn't find the library; assume it is in the system directories
-            dnl known to the linker and runtime loader. (All the system
-            dnl directories known to the linker should also be known to the
-            dnl runtime loader, otherwise the system is severely misconfigured.)
-            LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
-            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name"
-          fi
-        fi
-      fi
-    done
-  done
-  if test "X$rpathdirs" != "X"; then
-    if test -n "$hardcode_libdir_separator"; then
-      dnl Weird platform: only the last -rpath option counts, the user must
-      dnl pass all path elements in one option. We can arrange that for a
-      dnl single library, but not when more than one $LIBNAMEs are used.
-      alldirs=
-      for found_dir in $rpathdirs; do
-        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
-      done
-      dnl Note: hardcode_libdir_flag_spec uses $libdir and $wl.
-      acl_save_libdir="$libdir"
-      libdir="$alldirs"
-      eval flag=\"$hardcode_libdir_flag_spec\"
-      libdir="$acl_save_libdir"
-      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
-    else
-      dnl The -rpath options are cumulative.
-      for found_dir in $rpathdirs; do
-        acl_save_libdir="$libdir"
-        libdir="$found_dir"
-        eval flag=\"$hardcode_libdir_flag_spec\"
-        libdir="$acl_save_libdir"
-        LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
-      done
-    fi
-  fi
-  if test "X$ltrpathdirs" != "X"; then
-    dnl When using libtool, the option that works for both libraries and
-    dnl executables is -R. The -R options are cumulative.
-    for found_dir in $ltrpathdirs; do
-      LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir"
-    done
-  fi
-])
-
-dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR,
-dnl unless already present in VAR.
-dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes
-dnl contains two or three consecutive elements that belong together.
-AC_DEFUN([AC_LIB_APPENDTOVAR],
-[
-  for element in [$2]; do
-    haveit=
-    for x in $[$1]; do
-      AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
-      if test "X$x" = "X$element"; then
-        haveit=yes
-        break
-      fi
-    done
-    if test -z "$haveit"; then
-      [$1]="${[$1]}${[$1]:+ }$element"
-    fi
-  done
-])
-
-# lib-ld.m4 serial 3 (gettext-0.13)
-dnl Copyright (C) 1996-2003 Free Software Foundation, Inc.
-dnl This file is free software; the Free Software Foundation
-dnl gives unlimited permission to copy and/or distribute it,
-dnl with or without modifications, as long as this notice is preserved.
-
-dnl Subroutines of libtool.m4,
-dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision
-dnl with libtool.m4.
-
-dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no.
-AC_DEFUN([AC_LIB_PROG_LD_GNU],
-[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld,
-[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
-case `$LD -v 2>&1 </dev/null` in
-*GNU* | *'with BFD'*)
-  acl_cv_prog_gnu_ld=yes ;;
-*)
-  acl_cv_prog_gnu_ld=no ;;
-esac])
-with_gnu_ld=$acl_cv_prog_gnu_ld
-])
-
-dnl From libtool-1.4. Sets the variable LD.
-AC_DEFUN([AC_LIB_PROG_LD],
-[AC_ARG_WITH(gnu-ld,
-[  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]],
-test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
-AC_REQUIRE([AC_PROG_CC])dnl
-AC_REQUIRE([AC_CANONICAL_HOST])dnl
-# Prepare PATH_SEPARATOR.
-# The user is always right.
-if test "${PATH_SEPARATOR+set}" != set; then
-  echo "#! /bin/sh" >conf$$.sh
-  echo  "exit 0"   >>conf$$.sh
-  chmod +x conf$$.sh
-  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
-    PATH_SEPARATOR=';'
-  else
-    PATH_SEPARATOR=:
-  fi
-  rm -f conf$$.sh
-fi
-ac_prog=ld
-if test "$GCC" = yes; then
-  # Check if gcc -print-prog-name=ld gives a path.
-  AC_MSG_CHECKING([for ld used by GCC])
-  case $host in
-  *-*-mingw*)
-    # gcc leaves a trailing carriage return which upsets mingw
-    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
-  *)
-    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
-  esac
-  case $ac_prog in
-    # Accept absolute paths.
-    [[\\/]* | [A-Za-z]:[\\/]*)]
-      [re_direlt='/[^/][^/]*/\.\./']
-      # Canonicalize the path of ld
-      ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
-      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
-	ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
-      done
-      test -z "$LD" && LD="$ac_prog"
-      ;;
-  "")
-    # If it fails, then pretend we aren't using GCC.
-    ac_prog=ld
-    ;;
-  *)
-    # If it is relative, then search for the first ld in PATH.
-    with_gnu_ld=unknown
-    ;;
-  esac
-elif test "$with_gnu_ld" = yes; then
-  AC_MSG_CHECKING([for GNU ld])
-else
-  AC_MSG_CHECKING([for non-GNU ld])
-fi
-AC_CACHE_VAL(acl_cv_path_LD,
-[if test -z "$LD"; then
-  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
-  for ac_dir in $PATH; do
-    test -z "$ac_dir" && ac_dir=.
-    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
-      acl_cv_path_LD="$ac_dir/$ac_prog"
-      # Check to see if the program is GNU ld.  I'd rather use --version,
-      # but apparently some GNU ld's only accept -v.
-      # Break only if it was the GNU/non-GNU ld that we prefer.
-      case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
-      *GNU* | *'with BFD'*)
-	test "$with_gnu_ld" != no && break ;;
-      *)
-	test "$with_gnu_ld" != yes && break ;;
-      esac
-    fi
-  done
-  IFS="$ac_save_ifs"
-else
-  acl_cv_path_LD="$LD" # Let the user override the test with a path.
-fi])
-LD="$acl_cv_path_LD"
-if test -n "$LD"; then
-  AC_MSG_RESULT($LD)
-else
-  AC_MSG_RESULT(no)
-fi
-test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
-AC_LIB_PROG_LD_GNU
-])
-
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..a569614
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+autoreconf -fi;
+rm -Rf autom4te.cache;
diff --git a/configure.in b/configure.in
index f99292e..18d2716 100644
--- a/configure.in
+++ b/configure.in
@@ -6,74 +6,37 @@
 #	License as published by the Free Software Foundation version 2.1
 #	of the License.
 #
-# Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+# Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch>
 #
 
-AC_INIT(libnl, 1.1, tgraf@suug.ch)
-AC_CONFIG_HEADER(lib/defs.h)
-
-save_CFLAGS="${CFLAGS}"
-save_LDFLAGS="${LDFLAGS}"
-save_CPPFLAGS="${CPPFLAGS}"
+AC_INIT(libnl, 2.0, tgraf@suug.ch)
+AC_CONFIG_HEADERS([lib/defs.h])
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
 
 AC_PROG_CC
+AM_PROG_CC_C_O
 AC_PROG_INSTALL
+AM_PROG_LIBTOOL
+AM_PROG_LEX
+AC_PROG_YACC
 
 AC_C_CONST
 AC_C_INLINE
 
-#####################################################################
-##
-## libm check
-##
-#####################################################################
-M="No "
-AC_CHECK_LIB(m, pow,
-[
-  LIBM="-lm"
-  M="Yes"
-],[
-    echo
-    echo "*** Error: libm required ***"
-    echo
-    exit
-])
+AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
+	[Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
+	[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
+AC_SUBST([pkgconfigdir])
 
-#####################################################################
-##
-## verbose error strings
-##
-#####################################################################
-AC_ARG_ENABLE(verbose-errors,
-[  --enable-verbose-errors enable verbose errors (debugging)],[
-	if test x$enableval = xyes; then
-		AC_DEFINE_UNQUOTED(VERBOSE_ERRORS,"1",[verbose errors])
-	fi
-])
+AC_ARG_ENABLE([cli],
+	AS_HELP_STRING([--disable-cli], [Do not build command line interface utils]),
+	[enable_cli="$enableval"], [enable_cli="yes"])
+AM_CONDITIONAL([ENABLE_CLI], [test "$enable_cli" = "yes"])
 
-#####################################################################
-##
-## compile decisions
-##
-#####################################################################
-COMPILE_LIBNL="Yes "
-LIBNL_LIB="$LIBM"
+AC_CHECK_LIB([m], [pow], [], AC_MSG_ERROR([libm is required]))
 
-AC_SUBST(LIBNL_LIB)
-
-AC_OUTPUT([Makefile.opts libnl-1.pc doc/Doxyfile])
-
-#####################################################################
-##
-## status report
-##
-#####################################################################
-echo "
-----------------------------------------------------------------------
-SUMMARY:
-
-Included in Compilation:
-  libnl:   $COMPILE_LIBNL $LIBNL_LIB
-
-Dependencies:
-  libm             $M       (required)"
+AC_CONFIG_FILES([Makefile doc/Doxyfile doc/Makefile lib/Makefile
+	include/Makefile src/Makefile src/lib/Makefile \
+	libnl-2.0.pc include/netlink/version.h])
+AC_OUTPUT
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 7e00f51..8e311e3 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -463,6 +463,7 @@
 # with spaces.
 
 INPUT                  = ../lib \
+                         ../src/lib \
                          ../include/netlink
 
 # This tag can be used to specify the character encoding of the source files that 
diff --git a/doc/Makefile b/doc/Makefile
deleted file mode 100644
index 9c34e0f..0000000
--- a/doc/Makefile
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# doc/Makefile
-#
-# 	This library is free software; you can redistribute it and/or
-#	modify it under the terms of the GNU Lesser General Public
-#	License as published by the Free Software Foundation version 2.1
-#	of the License.
-#
-# Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
-#
-
-ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),)
-    include ../Makefile.opts
-endif
-
-export
-
-.PHONY: all gendoc clean distclean install
-
-all:
-	@true
-
-gendoc:
-	doxygen Doxyfile
-
-clean:
-	@true
-
-distclean:
-	$(RM) -f html/*
-
-install:
-	@true
-
-$(DEPS): ../Makefile.opts
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..040ff87
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,9 @@
+# -*- Makefile -*-
+
+.PHONY: gendoc
+
+gendoc:
+	doxygen Doxyfile;
+
+distclean-local:
+	rm -f html/*;
diff --git a/doc/libnl.css b/doc/libnl.css
index 42e3e27..22c4843 100644
--- a/doc/libnl.css
+++ b/doc/libnl.css
@@ -2,40 +2,42 @@
 	font-family: Geneva, Arial, Helvetica, sans-serif;
 }
 BODY,TD {
-       font-size: 90%;
+	font-size: 90%;
 }
 H1 {
 	text-align: center;
-       font-size: 160%;
+	font-size: 160%;
 }
 H2 {
-       font-size: 120%;
+	font-size: 120%;
 }
 H3 {
-       font-size: 100%;
+	font-size: 100%;
 }
-CAPTION { font-weight: bold }
+CAPTION { 
+	font-weight: bold 
+}
 DIV.qindex {
 	width: 100%;
-	background-color: #eeeeff;
-	border: 1px solid #b0b0b0;
+	background-color: #e8eef2;
+	border: 1px solid #84b0c7;
 	text-align: center;
 	margin: 2px;
 	padding: 2px;
 	line-height: 140%;
 }
-DIV.nav {
+DIV.navpath {
 	width: 100%;
-	background-color: #eeeeff;
-	border: 1px solid #b0b0b0;
+	background-color: #e8eef2;
+	border: 1px solid #84b0c7;
 	text-align: center;
 	margin: 2px;
 	padding: 2px;
 	line-height: 140%;
 }
 DIV.navtab {
-       background-color: #eeeeff;
-       border: 1px solid #b0b0b0;
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
        text-align: center;
        margin: 2px;
        margin-right: 15px;
@@ -70,17 +72,45 @@
 	background-color: #6666cc;
 	color: #ffffff;
 }
-A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff }
-A.el { text-decoration: none; font-weight: bold }
-A.elRef { font-weight: bold }
-A.code:link { text-decoration: none; font-weight: normal; color: #0000FF}
-A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF}
-A.codeRef:link { font-weight: normal; color: #0000FF}
-A.codeRef:visited { font-weight: normal; color: #0000FF}
-A:hover { text-decoration: none; background-color: #f2f2ff }
-DL.el { margin-left: -1cm }
+A.qindexHL:visited { 
+	text-decoration: none; 
+	background-color: #6666cc; 
+	color: #ffffff 
+}
+A.el { 
+	text-decoration: none; 
+	font-weight: bold 
+}
+A.elRef { 
+	font-weight: bold 
+}
+A.code:link { 
+	text-decoration: none; 
+	font-weight: normal; 
+	color: #0000FF
+}
+A.code:visited { 
+	text-decoration: none; 
+	font-weight: normal; 
+	color: #0000FF
+}
+A.codeRef:link { 
+	font-weight: normal; 
+	color: #0000FF
+}
+A.codeRef:visited { 
+	font-weight: normal; 
+	color: #0000FF
+}
+A:hover { 
+	text-decoration: none; 	
+	background-color: #f2f2ff 
+}
+DL.el { 
+	margin-left: -1cm 
+}
 .fragment {
-       font-family: Fixed, monospace;
+       font-family: monospace, fixed;
        font-size: 95%;
 }
 PRE.fragment {
@@ -95,22 +125,25 @@
 	padding-top: 4px;
 	padding-bottom: 4px;
 }
-DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px }
-TD.md { background-color: #F4F4FB; font-weight: bold; }
-TD.mdPrefix {
-       background-color: #F4F4FB;
-       color: #606060;
-	font-size: 80%;
+DIV.ah { 
+	background-color: black; 
+	font-weight: bold; 
+	color: #ffffff; 
+	margin-bottom: 3px; 
+	margin-top: 3px 
 }
-TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; }
-TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; }
+
 DIV.groupHeader {
        margin-left: 16px;
        margin-top: 12px;
        margin-bottom: 6px;
        font-weight: bold;
 }
-DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% }
+DIV.groupText { 
+	margin-left: 16px; 
+	font-style: italic; 
+	font-size: 90% 
+}
 BODY {
 	background: white;
 	color: black;
@@ -118,7 +151,7 @@
 	margin-left: 20px;
 }
 TD.indexkey {
-	background-color: #eeeeff;
+	background-color: #e8eef2;
 	font-weight: bold;
 	padding-right  : 10px;
 	padding-top    : 2px;
@@ -131,7 +164,7 @@
 	border: 1px solid #CCCCCC;
 }
 TD.indexvalue {
-	background-color: #eeeeff;
+	background-color: #e8eef2;
 	font-style: italic;
 	padding-right  : 10px;
 	padding-top    : 2px;
@@ -144,11 +177,16 @@
 	border: 1px solid #CCCCCC;
 }
 TR.memlist {
-   background-color: #f0f0f0; 
+	background-color: #f0f0f0; 
 }
-P.formulaDsp { text-align: center; }
-IMG.formulaDsp { }
-IMG.formulaInl { vertical-align: middle; }
+P.formulaDsp { 
+	text-align: center; 
+}
+IMG.formulaDsp {
+}
+IMG.formulaInl { 
+	vertical-align: middle; 
+}
 SPAN.keyword       { color: #008000 }
 SPAN.keywordtype   { color: #604020 }
 SPAN.keywordflow   { color: #e08000 }
@@ -156,16 +194,13 @@
 SPAN.preprocessor  { color: #806020 }
 SPAN.stringliteral { color: #002080 }
 SPAN.charliteral   { color: #008080 }
-.mdTable {
-	border: 1px solid #868686;
-	background-color: #F4F4FB;
-	width: 100%;
-}
-.mdRow {
-	padding: 8px 10px;
-}
+SPAN.vhdldigit     { color: #ff00ff }
+SPAN.vhdlchar      { color: #000000 }
+SPAN.vhdlkeyword   { color: #700070 }
+SPAN.vhdllogic     { color: #ff0000 }
+
 .mdescLeft {
-       padding: 0px 8px 4px 8px;
+	padding: 0px 8px 4px 8px;
 	font-size: 80%;
 	font-style: italic;
 	background-color: #FAFAFA;
@@ -176,7 +211,7 @@
 	margin: 0px;
 }
 .mdescRight {
-       padding: 0px 8px 4px 8px;
+        padding: 0px 8px 4px 8px;
 	font-size: 80%;
 	font-style: italic;
 	background-color: #FAFAFA;
@@ -273,38 +308,166 @@
 	border-right-style: none;
 	border-bottom-style: none;
 	border-left-style: none;
-       color: #606060;
+	color: #606060;
 	background-color: #FAFAFA;
 	font-size: 80%;
 }
-.search     { color: #003399;
-              font-weight: bold;
+.search { 
+	color: #003399;
+	font-weight: bold;
 }
 FORM.search {
-              margin-bottom: 0px;
-              margin-top: 0px;
+	margin-bottom: 0px;
+	margin-top: 0px;
 }
-INPUT.search { font-size: 75%;
-               color: #000080;
-               font-weight: normal;
-               background-color: #eeeeff;
+INPUT.search { 
+	font-size: 75%;
+	color: #000080;
+	font-weight: normal;
+	background-color: #e8eef2;
 }
-TD.tiny      { font-size: 75%;
+TD.tiny { 
+	font-size: 75%;
 }
 a {
-	color: #252E78;
+	color: #1A41A8;
 }
 a:visited {
-	color: #3D2185;
+	color: #2A3798;
 }
-.dirtab { padding: 4px;
-          border-collapse: collapse;
-          border: 1px solid #b0b0b0;
+.dirtab { 
+	padding: 4px;
+	border-collapse: collapse;
+	border: 1px solid #84b0c7;
 }
-TH.dirtab { background: #eeeeff;
-            font-weight: bold;
+TH.dirtab { 
+	background: #e8eef2;
+	font-weight: bold;
 }
-HR { height: 1px;
-     border: none;
-     border-top: 1px solid black;
+HR { 
+	height: 1px;
+	border: none;
+	border-top: 1px solid black;
 }
+
+/* Style for detailed member documentation */
+.memtemplate {
+	font-size: 80%;
+	color: #606060;
+	font-weight: normal;
+	margin-left: 3px;
+} 
+.memnav { 
+	background-color: #e8eef2;
+	border: 1px solid #84b0c7;
+	text-align: center;
+	margin: 2px;
+	margin-right: 15px;
+	padding: 2px;
+}
+.memitem {
+	padding: 4px;
+	background-color: #eef3f5;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #dedeee;
+	-moz-border-radius: 8px 8px 8px 8px;
+}
+.memname {
+	white-space: nowrap;
+	font-weight: bold;
+}
+.memdoc{
+	padding-left: 10px;
+}
+.memproto {
+	background-color: #d5e1e8;
+	width: 100%;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #84b0c7;
+	font-weight: bold;
+	-moz-border-radius: 8px 8px 8px 8px;
+}
+.paramkey {
+	text-align: right;
+}
+.paramtype {
+	white-space: nowrap;
+}
+.paramname {
+	color: #602020;
+	font-style: italic;
+	white-space: nowrap;
+}
+/* End Styling for detailed member documentation */
+
+/* for the tree view */
+.ftvtree {
+	font-family: sans-serif;
+	margin:0.5em;
+}
+/* these are for tree view when used as main index */
+.directory { 
+	font-size: 9pt; 
+	font-weight: bold; 
+}
+.directory h3 { 
+	margin: 0px; 
+	margin-top: 1em; 
+	font-size: 11pt; 
+}
+
+/* The following two styles can be used to replace the root node title */
+/* with an image of your choice.  Simply uncomment the next two styles, */
+/* specify the name of your image and be sure to set 'height' to the */
+/* proper pixel height of your image. */
+
+/* .directory h3.swap { */
+/* 	height: 61px; */
+/* 	background-repeat: no-repeat; */
+/* 	background-image: url("yourimage.gif"); */
+/* } */
+/* .directory h3.swap span { */
+/* 	display: none; */
+/* } */
+
+.directory > h3 { 
+	margin-top: 0; 
+}
+.directory p { 
+	margin: 0px; 
+	white-space: nowrap; 
+}
+.directory div { 
+	display: none; 
+	margin: 0px; 
+}
+.directory img { 
+	vertical-align: -30%; 
+}
+/* these are for tree view when not used as main index */
+.directory-alt { 
+	font-size: 100%; 
+	font-weight: bold; 
+}
+.directory-alt h3 { 
+	margin: 0px; 
+	margin-top: 1em; 
+	font-size: 11pt; 
+}
+.directory-alt > h3 { 
+	margin-top: 0; 
+}
+.directory-alt p { 
+	margin: 0px; 
+	white-space: nowrap; 
+}
+.directory-alt div { 
+	display: none; 
+	margin: 0px; 
+}
+.directory-alt img { 
+	vertical-align: -30%; 
+}
+
diff --git a/etc/pktloc b/etc/pktloc
new file mode 100644
index 0000000..db36d40
--- /dev/null
+++ b/etc/pktloc
@@ -0,0 +1,44 @@
+#
+# Location definitions for packet matching
+#
+
+# name		alignment	offset		mask
+ip.version	u8		net+0		0xF0
+ip.hdrlen	u8		net+0		0x0F
+ip.diffserv	u8		net+1
+ip.length	u16		net+2
+ip.id		u16		net+4
+ip.df		u8		net+6		0x40
+ip.mf		u8		net+6		0x20
+ip.offset	u16		net+6		0x1FFF
+ip.ttl		u8		net+8
+ip.proto	u8		net+9
+ip.chksum	u16		net+10
+ip.src		u32		net+12
+ip.dst		u32		net+16
+
+
+#
+# Transmission Control Protocol (TCP)
+#
+# name		alignment	offset		mask
+tcp.sport	u16		tcp+0
+tcp.dport	u16		tcp+2
+tcp.seq		u32		tcp+4
+tcp.ack		u32		tcp+8
+tcp.off		u8		tcp+12		0xF0
+tcp.reserved	u8		tcp+12		0x0F
+# FLAGS
+tcp.win		u16		tcp+14
+tcp.csum	u16		tcp+16
+tcp.urg		u16		tcp+18
+tcp.opts	u32		tcp+20
+
+#
+# User Datagram Protocol (UDP)
+#
+# name		alignment	offset		mask
+udp.sport	u16		tcp+0
+udp.dport	u16		tcp+2
+udp.length	u16		tcp+4
+udp.csum	u16		tcp+6
diff --git a/include/Makefile b/include/Makefile
deleted file mode 100644
index a2b23e0..0000000
--- a/include/Makefile
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# include/Makefile
-#
-# 	This library is free software; you can redistribute it and/or
-#	modify it under the terms of the GNU Lesser General Public
-#	License as published by the Free Software Foundation version 2.1
-#	of the License.
-#
-# Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
-#
-
-ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),)
-	include ../Makefile.opts
-endif
-
-.PHONY: all clean install 
-
-all:
-	@true
-
-clean:
-	@true
-
-distclean:
-	@true
-
-install:
-	mkdir -p $(DESTDIR)$(includedir)/netlink/route/sch/
-	mkdir -p $(DESTDIR)$(includedir)/netlink/route/cls/
-	mkdir -p $(DESTDIR)$(includedir)/netlink/genl/
-	mkdir -p $(DESTDIR)$(includedir)/netlink/fib_lookup/
-	install -m 0644 netlink/*.h $(DESTDIR)$(includedir)/netlink/
-	install -m 0644 netlink/route/*.h $(DESTDIR)$(includedir)/netlink/route/
-	install -m 0644 netlink/route/sch/*.h $(DESTDIR)$(includedir)/netlink/route/sch/
-	install -m 0644 netlink/route/cls/*.h $(DESTDIR)$(includedir)/netlink/route/cls/
-	install -m 0644 netlink/genl/*.h $(DESTDIR)$(includedir)/netlink/genl/
-	install -m 0644 netlink/fib_lookup/*.h $(DESTDIR)$(includedir)/netlink/fib_lookup/
-
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..8815a6e
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,63 @@
+# -*- Makefile -*-
+
+nobase_include_HEADERS = \
+	netlink/fib_lookup/lookup.h \
+	netlink/fib_lookup/request.h \
+	netlink/genl/ctrl.h \
+	netlink/genl/family.h \
+	netlink/genl/genl.h \
+	netlink/genl/mngt.h \
+	netlink/netfilter/ct.h \
+	netlink/netfilter/log.h \
+	netlink/netfilter/log_msg.h \
+	netlink/netfilter/netfilter.h \
+	netlink/netfilter/nfnl.h \
+	netlink/netfilter/queue.h \
+	netlink/netfilter/queue_msg.h \
+	netlink/addr.h \
+	netlink/attr.h \
+	netlink/cache-api.h \
+	netlink/cache.h \
+	netlink/data.h \
+	netlink/errno.h \
+	netlink/handlers.h \
+	netlink/list.h \
+	netlink/msg.h \
+	netlink/netlink-compat.h \
+	netlink/netlink-kernel.h \
+	netlink/netlink.h \
+	netlink/object-api.h \
+	netlink/object.h \
+	netlink/route/cls/fw.h \
+	netlink/route/cls/police.h \
+	netlink/route/cls/u32.h \
+	netlink/route/link/info-api.h \
+	netlink/route/link/vlan.h \
+	netlink/route/sch/cbq.h \
+	netlink/route/sch/dsmark.h \
+	netlink/route/sch/fifo.h \
+	netlink/route/sch/htb.h \
+	netlink/route/sch/netem.h \
+	netlink/route/sch/prio.h \
+	netlink/route/sch/red.h \
+	netlink/route/sch/sfq.h \
+	netlink/route/sch/tbf.h \
+	netlink/route/addr.h \
+	netlink/route/class-modules.h \
+	netlink/route/class.h \
+	netlink/route/classifier-modules.h \
+	netlink/route/classifier.h \
+	netlink/route/link.h \
+	netlink/route/neighbour.h \
+	netlink/route/neightbl.h \
+	netlink/route/nexthop.h \
+	netlink/route/qdisc-modules.h \
+	netlink/route/qdisc.h \
+	netlink/route/route.h \
+	netlink/route/rtnl.h \
+	netlink/route/rule.h \
+	netlink/route/tc.h \
+	netlink/socket.h \
+	netlink/types.h \
+	netlink/utils.h \
+	netlink/version.h
diff --git a/include/linux/if_bad.h b/include/linux/if_bad.h
index 4c1bcfe..857ce1e 100644
--- a/include/linux/if_bad.h
+++ b/include/linux/if_bad.h
@@ -107,7 +107,7 @@
 };
 
 /*
- *	Device mapping structure. I'd just gone off and designed a 
+ *	Device mapping structure. I'd just gone off and designed a
  *	beautiful scheme using only loadable modules with arguments
  *	for driver options and along come the PCMCIA people 8)
  *
@@ -116,11 +116,11 @@
  *	being very small might be worth keeping for clean configuration.
  */
 
-struct ifmap 
+struct ifmap
 {
 	unsigned long mem_start;
 	unsigned long mem_end;
-	unsigned short base_addr; 
+	unsigned short base_addr;
 	unsigned char irq;
 	unsigned char dma;
 	unsigned char port;
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
new file mode 100644
index 0000000..068cd7b
--- /dev/null
+++ b/include/linux/if_vlan.h
@@ -0,0 +1,61 @@
+/*
+ * VLAN		An implementation of 802.1Q VLAN tagging.
+ *
+ * Authors:	Ben Greear <greearb@candelatech.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.
+ *
+ */
+
+#ifndef _LINUX_IF_VLAN_H_
+#define _LINUX_IF_VLAN_H_
+
+
+/* VLAN IOCTLs are found in sockios.h */
+
+/* Passed in vlan_ioctl_args structure to determine behaviour. */
+enum vlan_ioctl_cmds {
+	ADD_VLAN_CMD,
+	DEL_VLAN_CMD,
+	SET_VLAN_INGRESS_PRIORITY_CMD,
+	SET_VLAN_EGRESS_PRIORITY_CMD,
+	GET_VLAN_INGRESS_PRIORITY_CMD,
+	GET_VLAN_EGRESS_PRIORITY_CMD,
+	SET_VLAN_NAME_TYPE_CMD,
+	SET_VLAN_FLAG_CMD,
+	GET_VLAN_REALDEV_NAME_CMD, /* If this works, you know it's a VLAN device, btw */
+	GET_VLAN_VID_CMD /* Get the VID of this VLAN (specified by name) */
+};
+
+enum vlan_flags {
+	VLAN_FLAG_REORDER_HDR	= 0x1,
+};
+
+enum vlan_name_types {
+	VLAN_NAME_TYPE_PLUS_VID, /* Name will look like:  vlan0005 */
+	VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like:  eth1.0005 */
+	VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like:  vlan5 */
+	VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like:  eth0.5 */
+	VLAN_NAME_TYPE_HIGHEST
+};
+
+struct vlan_ioctl_args {
+	int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */
+	char device1[24];
+
+        union {
+		char device2[24];
+		int VID;
+		unsigned int skb_priority;
+		unsigned int name_type;
+		unsigned int bind_type;
+		unsigned int flag; /* Matches vlan_dev_info flags */
+        } u;
+
+	short vlan_qos;
+};
+
+#endif /* !(_LINUX_IF_VLAN_H_) */
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
new file mode 100644
index 0000000..0750ca6
--- /dev/null
+++ b/include/linux/netfilter.h
@@ -0,0 +1,39 @@
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
+
+
+/* Responses from hook functions. */
+#define NF_DROP 0
+#define NF_ACCEPT 1
+#define NF_STOLEN 2
+#define NF_QUEUE 3
+#define NF_REPEAT 4
+#define NF_STOP 5
+#define NF_MAX_VERDICT NF_STOP
+
+/* we overload the higher bits for encoding auxiliary data such as the queue
+ * number. Not nice, but better than additional function arguments. */
+#define NF_VERDICT_MASK 0x0000ffff
+#define NF_VERDICT_BITS 16
+
+#define NF_VERDICT_QMASK 0xffff0000
+#define NF_VERDICT_QBITS 16
+
+#define NF_QUEUE_NR(x) (((x << NF_VERDICT_QBITS) & NF_VERDICT_QMASK) | NF_QUEUE)
+
+/* only for userspace compatibility */
+/* Generic cache responses from hook functions.
+   <= 0x2000 is used for protocol-flags. */
+#define NFC_UNKNOWN 0x4000
+#define NFC_ALTERED 0x8000
+
+enum nf_inet_hooks {
+	NF_INET_PRE_ROUTING,
+	NF_INET_LOCAL_IN,
+	NF_INET_FORWARD,
+	NF_INET_LOCAL_OUT,
+	NF_INET_POST_ROUTING,
+	NF_INET_NUMHOOKS,
+};
+
+#endif /*__LINUX_NETFILTER_H*/
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 20835a1..4bb4c44 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -10,7 +10,7 @@
 #define NF_NETLINK_CONNTRACK_EXP_NEW		0x00000008
 #define NF_NETLINK_CONNTRACK_EXP_UPDATE		0x00000010
 #define NF_NETLINK_CONNTRACK_EXP_DESTROY	0x00000020
-#define NF_NETLINK_CONNTRACK_QUOTA		0x00000040
+#define NF_NETLINK_ACCT_QUOTA			0x00000040
 #endif
 
 enum nfnetlink_groups {
@@ -28,8 +28,10 @@
 #define NFNLGRP_CONNTRACK_EXP_UPDATE	NFNLGRP_CONNTRACK_EXP_UPDATE
 	NFNLGRP_CONNTRACK_EXP_DESTROY,
 #define NFNLGRP_CONNTRACK_EXP_DESTROY	NFNLGRP_CONNTRACK_EXP_DESTROY
-	NFNLGRP_CONNTRACK_QUOTA,
-#define NFNLGRP_CONNTRACK_QUOTA		NFNLGRP_CONNTRACK_QUOTA
+	NFNLGRP_NFTABLES,
+#define NFNLGRP_NFTABLES		NFNLGRP_NFTABLES
+	NFNLGRP_ACCT_QUOTA,
+#define NFNLGRP_ACCT_QUOTA		NFNLGRP_ACCT_QUOTA
 	__NFNLGRP_MAX,
 };
 #define NFNLGRP_MAX	(__NFNLGRP_MAX - 1)
diff --git a/include/linux/netfilter/nfnetlink_log.h b/include/linux/netfilter/nfnetlink_log.h
index 2de5df9..38fafc1 100644
--- a/include/linux/netfilter/nfnetlink_log.h
+++ b/include/linux/netfilter/nfnetlink_log.h
@@ -51,6 +51,7 @@
 	NFULA_UID,			/* user id of socket */
 	NFULA_SEQ,			/* instance-local sequence number */
 	NFULA_SEQ_GLOBAL,		/* global sequence number */
+	NFULA_GID,			/* group id of socket */
 
 	__NFULA_MAX
 };
diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h
new file mode 100644
index 0000000..bf7cfb6
--- /dev/null
+++ b/include/linux/netfilter/nfnetlink_queue.h
@@ -0,0 +1,94 @@
+#ifndef _NFNETLINK_QUEUE_H
+#define _NFNETLINK_QUEUE_H
+
+#include <linux/types.h>
+#include <linux/netfilter/nfnetlink.h>
+
+#ifndef aligned_be64
+#define aligned_be64 u_int64_t __attribute__((aligned(8)))
+#endif
+
+enum nfqnl_msg_types {
+	NFQNL_MSG_PACKET,		/* packet from kernel to userspace */
+	NFQNL_MSG_VERDICT,		/* verdict from userspace to kernel */
+	NFQNL_MSG_CONFIG,		/* connect to a particular queue */
+
+	NFQNL_MSG_MAX
+};
+
+struct nfqnl_msg_packet_hdr {
+	__be32		packet_id;	/* unique ID of packet in queue */
+	__be16		hw_protocol;	/* hw protocol (network order) */
+	u_int8_t	hook;		/* netfilter hook */
+} __attribute__ ((packed));
+
+struct nfqnl_msg_packet_hw {
+	__be16		hw_addrlen;
+	u_int16_t	_pad;
+	u_int8_t	hw_addr[8];
+};
+
+struct nfqnl_msg_packet_timestamp {
+	aligned_be64	sec;
+	aligned_be64	usec;
+};
+
+enum nfqnl_attr_type {
+	NFQA_UNSPEC,
+	NFQA_PACKET_HDR,
+	NFQA_VERDICT_HDR,		/* nfqnl_msg_verdict_hrd */
+	NFQA_MARK,			/* u_int32_t nfmark */
+	NFQA_TIMESTAMP,			/* nfqnl_msg_packet_timestamp */
+	NFQA_IFINDEX_INDEV,		/* u_int32_t ifindex */
+	NFQA_IFINDEX_OUTDEV,		/* u_int32_t ifindex */
+	NFQA_IFINDEX_PHYSINDEV,		/* u_int32_t ifindex */
+	NFQA_IFINDEX_PHYSOUTDEV,	/* u_int32_t ifindex */
+	NFQA_HWADDR,			/* nfqnl_msg_packet_hw */
+	NFQA_PAYLOAD,			/* opaque data payload */
+
+	__NFQA_MAX
+};
+#define NFQA_MAX (__NFQA_MAX - 1)
+
+struct nfqnl_msg_verdict_hdr {
+	__be32 verdict;
+	__be32 id;
+};
+
+
+enum nfqnl_msg_config_cmds {
+	NFQNL_CFG_CMD_NONE,
+	NFQNL_CFG_CMD_BIND,
+	NFQNL_CFG_CMD_UNBIND,
+	NFQNL_CFG_CMD_PF_BIND,
+	NFQNL_CFG_CMD_PF_UNBIND,
+};
+
+struct nfqnl_msg_config_cmd {
+	u_int8_t	command;	/* nfqnl_msg_config_cmds */
+	u_int8_t	_pad;
+	__be16		pf;		/* AF_xxx for PF_[UN]BIND */
+};
+
+enum nfqnl_config_mode {
+	NFQNL_COPY_NONE,
+	NFQNL_COPY_META,
+	NFQNL_COPY_PACKET,
+};
+
+struct nfqnl_msg_config_params {
+	__be32		copy_range;
+	u_int8_t	copy_mode;	/* enum nfqnl_config_mode */
+} __attribute__ ((packed));
+
+
+enum nfqnl_attr_config {
+	NFQA_CFG_UNSPEC,
+	NFQA_CFG_CMD,			/* nfqnl_msg_config_cmd */
+	NFQA_CFG_PARAMS,		/* nfqnl_msg_config_params */
+	NFQA_CFG_QUEUE_MAXLEN,		/* u_int32_t */
+	__NFQA_CFG_MAX
+};
+#define NFQA_CFG_MAX (__NFQA_CFG_MAX-1)
+
+#endif /* _NFNETLINK_QUEUE_H */
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index 30b8571..3c842ed 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -1,6 +1,7 @@
 #ifndef __LINUX_PKT_CLS_H
 #define __LINUX_PKT_CLS_H
 
+#include <linux/types.h>
 #include <linux/pkt_sched.h>
 
 /* I think i could have done better macros ; for now this is stolen from
@@ -201,8 +202,8 @@
 
 struct tc_u32_key
 {
-	__u32		mask;
-	__u32		val;
+	__be32		mask;
+	__be32		val;
 	int		off;
 	int		offmask;
 };
@@ -213,12 +214,12 @@
 	unsigned char		offshift;
 	unsigned char		nkeys;
 
-	__u16			offmask;
+	__be16			offmask;
 	__u16			off;
 	short			offoff;
 
 	short			hoff;
-	__u32			hmask;
+	__be32			hmask;
 	struct tc_u32_key	keys[0];
 };
 
@@ -328,6 +329,58 @@
 
 #define TCA_TCINDEX_MAX     (__TCA_TCINDEX_MAX - 1)
 
+/* Flow filter */
+
+enum
+{
+	FLOW_KEY_SRC,
+	FLOW_KEY_DST,
+	FLOW_KEY_PROTO,
+	FLOW_KEY_PROTO_SRC,
+	FLOW_KEY_PROTO_DST,
+	FLOW_KEY_IIF,
+	FLOW_KEY_PRIORITY,
+	FLOW_KEY_MARK,
+	FLOW_KEY_NFCT,
+	FLOW_KEY_NFCT_SRC,
+	FLOW_KEY_NFCT_DST,
+	FLOW_KEY_NFCT_PROTO_SRC,
+	FLOW_KEY_NFCT_PROTO_DST,
+	FLOW_KEY_RTCLASSID,
+	FLOW_KEY_SKUID,
+	FLOW_KEY_SKGID,
+	FLOW_KEY_VLAN_TAG,
+	__FLOW_KEY_MAX,
+};
+
+#define FLOW_KEY_MAX	(__FLOW_KEY_MAX - 1)
+
+enum
+{
+	FLOW_MODE_MAP,
+	FLOW_MODE_HASH,
+};
+
+enum
+{
+	TCA_FLOW_UNSPEC,
+	TCA_FLOW_KEYS,
+	TCA_FLOW_MODE,
+	TCA_FLOW_BASECLASS,
+	TCA_FLOW_RSHIFT,
+	TCA_FLOW_ADDEND,
+	TCA_FLOW_MASK,
+	TCA_FLOW_XOR,
+	TCA_FLOW_DIVISOR,
+	TCA_FLOW_ACT,
+	TCA_FLOW_POLICE,
+	TCA_FLOW_EMATCHES,
+	TCA_FLOW_PERTURB,
+	__TCA_FLOW_MAX
+};
+
+#define TCA_FLOW_MAX	(__TCA_FLOW_MAX - 1)
+
 /* Basic filter */
 
 enum
@@ -342,6 +395,20 @@
 
 #define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
 
+
+/* Cgroup classifier */
+
+enum
+{
+	TCA_CGROUP_UNSPEC,
+	TCA_CGROUP_ACT,
+	TCA_CGROUP_POLICE,
+	TCA_CGROUP_EMATCHES,
+	__TCA_CGROUP_MAX,
+};
+
+#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
+
 /* Extended Matches */
 
 struct tcf_ematch_tree_hdr
@@ -409,7 +476,8 @@
 #define	TCF_EM_U32		3
 #define	TCF_EM_META		4
 #define	TCF_EM_TEXT		5
-#define	TCF_EM_MAX		5
+#define        TCF_EM_VLAN		6
+#define	TCF_EM_MAX		6
 
 enum
 {
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 29d358c..2473dbe 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -97,6 +97,16 @@
 	RTM_SETNEIGHTBL,
 #define RTM_SETNEIGHTBL	RTM_SETNEIGHTBL
 
+	RTM_NEWNDUSEROPT = 68,
+#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT
+
+	RTM_NEWADDRLABEL = 72,
+#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
+	RTM_DELADDRLABEL,
+#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
+	RTM_GETADDRLABEL,
+#define RTM_GETADDRLABEL RTM_GETADDRLABEL
+
 	__RTM_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -257,12 +267,13 @@
 	RTA_PREFSRC,
 	RTA_METRICS,
 	RTA_MULTIPATH,
-	RTA_PROTOINFO,
+	RTA_PROTOINFO, /* no longer used */
 	RTA_FLOW,
 	RTA_CACHEINFO,
-	RTA_SESSION,
-	RTA_MP_ALGO,
+	RTA_SESSION, /* no longer used */
+	RTA_MP_ALGO, /* no longer used */
 	RTA_TABLE,
+	RTA_GENERATION,
 	__RTA_MAX
 };
 
@@ -351,6 +362,8 @@
 #define RTAX_INITCWND RTAX_INITCWND
 	RTAX_FEATURES,
 #define RTAX_FEATURES RTAX_FEATURES
+	RTAX_RTO_MIN,
+#define RTAX_RTO_MIN RTAX_RTO_MIN
 	__RTAX_MAX
 };
 
@@ -477,6 +490,32 @@
 #define TCA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg))))
 #define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg))
 
+/********************************************************************
+ *		Neighbor Discovery userland options
+ ****/
+
+struct nduseroptmsg
+{
+	unsigned char	nduseropt_family;
+	unsigned char	nduseropt_pad1;
+	unsigned short	nduseropt_opts_len;	/* Total length of options */
+	int		nduseropt_ifindex;
+	__u8		nduseropt_icmp_type;
+	__u8		nduseropt_icmp_code;
+	unsigned short	nduseropt_pad2;
+	unsigned int	nduseropt_pad3;
+	/* Followed by one or more ND options */
+};
+
+enum
+{
+	NDUSEROPT_UNSPEC,
+	NDUSEROPT_SRCADDR,
+	__NDUSEROPT_MAX
+};
+
+#define NDUSEROPT_MAX	(__NDUSEROPT_MAX - 1)
+
 #ifndef __KERNEL__
 /* RTnetlink multicast groups - backwards compatibility for userspace */
 #define RTMGRP_LINK		1
@@ -540,6 +579,8 @@
 #define RTNLGRP_IPV6_PREFIX	RTNLGRP_IPV6_PREFIX
 	RTNLGRP_IPV6_RULE,
 #define RTNLGRP_IPV6_RULE	RTNLGRP_IPV6_RULE
+	RTNLGRP_ND_USEROPT,
+#define RTNLGRP_ND_USEROPT	RTNLGRP_ND_USEROPT
 	__RTNLGRP_MAX
 };
 #define RTNLGRP_MAX	(__RTNLGRP_MAX - 1)
@@ -556,4 +597,6 @@
 #define TCA_ACT_TAB 1 /* attr type must be >=1 */	
 #define TCAA_MAX 1
 
+/* End of information exported to user level */
+
 #endif	/* __LINUX_RTNETLINK_H */
diff --git a/include/netlink-local.h b/include/netlink-local.h
index 05b4093..7e33119 100644
--- a/include/netlink-local.h
+++ b/include/netlink-local.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_LOCAL_H_
@@ -23,9 +23,11 @@
 #include <stdarg.h>
 #include <ctype.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/socket.h>
 #include <inttypes.h>
 #include <assert.h>
+#include <limits.h>
 
 #include <arpa/inet.h>
 #include <netdb.h>
@@ -81,247 +83,22 @@
 		assert(0);	\
 	} while (0)
 
-#define RET_ERR(R, E)                    \
-    do {                                 \
-		errno = E;                       \
-		return -R;                       \
-	} while (0)
-
-extern int __nl_error(int, const char *, unsigned int,
-	const char *, const char *, ...);
-
 extern int __nl_read_num_str_file(const char *path,
 				  int (*cb)(long, const char *));
 
-#ifdef NL_ERROR_ASSERT
-#include <assert.h>
-static inline int __assert_error(const char *file, int line, char *func,
-	const char *fmt, ...)
-{
-	va_list args;
-	fprintf(stderr, "%s:%d:%s: ", file, line, func);
-	va_start(args, fmt);
-	vfprintf(stderr, fmt, args);
-	va_end(args);
-	fprintf(stderr, "\n");
-	assert(0);
-	return 0;
-}
-#define nl_error(E, FMT,ARG...) \
-	__assert_error(__FILE__, __LINE__, __FUNCTION__, FMT, ##ARG)
+extern int __trans_list_add(int, const char *, struct nl_list_head *);
+extern void __trans_list_clear(struct nl_list_head *);
 
-#else
-#define nl_error(E, FMT,ARG...) \
-	__nl_error(E, __FILE__, __LINE__, __FUNCTION__, FMT, ##ARG)
+extern char *__type2str(int, char *, size_t, struct trans_tbl *, size_t);
+extern int __str2type(const char *, struct trans_tbl *, size_t);
 
-#endif
+extern char *__list_type2str(int, char *, size_t, struct nl_list_head *);
+extern int __list_str2type(const char *, struct nl_list_head *);
 
-#define nl_errno(E)	nl_error(E, NULL)
+extern char *__flags2str(int, char *, size_t, struct trans_tbl *, size_t);
+extern int __str2flags(const char *, struct trans_tbl *, size_t);
 
-/* backwards compat */
-#define dp_new_line(params, line)	nl_new_line(params, line)
-#define dp_dump(params, fmt, arg...)	nl_dump(params, fmt, ##arg)
-
-static inline int __trans_list_add(int i, const char *a,
-				   struct nl_list_head *head)
-{
-	struct trans_list *tl;
-
-	tl = calloc(1, sizeof(*tl));
-	if (!tl)
-		return nl_errno(ENOMEM);
-
-	tl->i = i;
-	tl->a = strdup(a);
-
-	nl_list_add_tail(&tl->list, head);
-
-	return 0;
-}
-
-static inline void __trans_list_clear(struct nl_list_head *head)
-{
-	struct trans_list *tl, *next;
-
-	nl_list_for_each_entry_safe(tl, next, head, list) {
-		free(tl->a);
-		free(tl);
-	}
-}
-
-static inline char *__type2str(int type, char *buf, size_t len,
-			       struct trans_tbl *tbl, size_t tbl_len)
-{
-	int i;
-	for (i = 0; i < tbl_len; i++) {
-		if (tbl[i].i == type) {
-			snprintf(buf, len, "%s", tbl[i].a);
-			return buf;
-		}
-	}
-
-	snprintf(buf, len, "0x%x", type);
-	return buf;
-}
-
-static inline char *__list_type2str(int type, char *buf, size_t len,
-				    struct nl_list_head *head)
-{
-	struct trans_list *tl;
-
-	nl_list_for_each_entry(tl, head, list) {
-		if (tl->i == type) {
-			snprintf(buf, len, "%s", tl->a);
-			return buf;
-		}
-	}
-
-	snprintf(buf, len, "0x%x", type);
-	return buf;
-}
-
-static inline char *__flags2str(int flags, char *buf, size_t len,
-				struct trans_tbl *tbl, size_t tbl_len)
-{
-	int i;
-	int tmp = flags;
-
-	memset(buf, 0, len);
-	
-	for (i = 0; i < tbl_len; i++) {
-		if (tbl[i].i & tmp) {
-			tmp &= ~tbl[i].i;
-			strncat(buf, tbl[i].a, len - strlen(buf) - 1);
-			if ((tmp & flags))
-				strncat(buf, ",", len - strlen(buf) - 1);
-		}
-	}
-
-	return buf;
-}
-
-static inline int __str2type(const char *buf, struct trans_tbl *tbl,
-			     size_t tbl_len)
-{
-	unsigned long l;
-	char *end;
-	int i;
-
-	if (*buf == '\0')
-		return -1;
-
-	for (i = 0; i < tbl_len; i++)
-		if (!strcasecmp(tbl[i].a, buf))
-			return tbl[i].i;
-
-	l = strtoul(buf, &end, 0);
-	if (l == ULONG_MAX || *end != '\0')
-		return -1;
-
-	return (int) l;
-}
-
-static inline int __list_str2type(const char *buf, struct nl_list_head *head)
-{
-	struct trans_list *tl;
-	unsigned long l;
-	char *end;
-
-	if (*buf == '\0')
-		return -1;
-
-	nl_list_for_each_entry(tl, head, list) {
-		if (!strcasecmp(tl->a, buf))
-			return tl->i;
-	}
-
-	l = strtoul(buf, &end, 0);
-	if (l == ULONG_MAX || *end != '\0')
-		return -1;
-
-	return (int) l;
-}
-
-static inline int __str2flags(const char *buf, struct trans_tbl *tbl,
-			      size_t tbl_len)
-{
-	int i, flags = 0, len;
-	char *p = (char *) buf, *t;
-
-	for (;;) {
-		if (*p == ' ')
-			p++;
-	
-		t = strchr(p, ',');
-		len = t ? t - p : strlen(p);
-		for (i = 0; i < tbl_len; i++)
-			if (!strncasecmp(tbl[i].a, p, len))
-				flags |= tbl[i].i;
-
-		if (!t)
-			return flags;
-
-		p = ++t;
-	}
-
-	return 0;
-}
-
-static inline void __dp_dump(struct nl_dump_params *parms, const char *fmt,
-			     va_list args)
-{
-	if (parms->dp_fd)
-		vfprintf(parms->dp_fd, fmt, args);
-	else if (parms->dp_buf || parms->dp_cb) {
-		char *buf = NULL;
-		vasprintf(&buf, fmt, args);
-		if (parms->dp_cb)
-			parms->dp_cb(parms, buf);
-		else
-			strncat(parms->dp_buf, buf,
-			        parms->dp_buflen - strlen(parms->dp_buf) - 1);
-		free(buf);
-	}
-}
-
-static inline void dp_dump_line(struct nl_dump_params *parms, int line,
-				const char *fmt, ...)
-{
-	va_list args;
-
-	nl_new_line(parms, line);
-
-	va_start(args, fmt);
-	__dp_dump(parms, fmt, args);
-	va_end(args);
-}
-
-static inline void dump_from_ops(struct nl_object *obj,
-				 struct nl_dump_params *params)
-{
-	int type = params->dp_type;
-
-	if (type < 0 || type > NL_DUMP_MAX)
-		BUG();
-
-	if (params->dp_dump_msgtype) {
-#if 0
-		/* XXX */
-		char buf[64];
-
-		dp_dump_line(params, 0, "%s ",
-			     nl_cache_mngt_type2name(obj->ce_ops,
-			     			     obj->ce_ops->co_protocol,
-						     obj->ce_msgtype,
-						     buf, sizeof(buf)));
-#endif
-		params->dp_pre_dump = 1;
-	} else
-		dp_new_line(params, 0);
-
-	if (obj->ce_ops->oo_dump[type])
-		obj->ce_ops->oo_dump[type](obj, params);
-}
+extern void dump_from_ops(struct nl_object *, struct nl_dump_params *);
 
 static inline struct nl_cache *dp_cache(struct nl_object *obj)
 {
@@ -340,9 +117,8 @@
 
 #define __init __attribute__ ((constructor))
 #define __exit __attribute__ ((destructor))
-
-#define P_ACCEPT 0
-#define P_IGNORE 0
+#undef __deprecated
+#define __deprecated __attribute__ ((deprecated))
 
 #define min(x,y) ({ \
 	typeof(x) _x = (x);	\
@@ -396,4 +172,12 @@
 		END_OF_MSGTYPES_LIST, \
 	}
 
+static inline int wait_for_ack(struct nl_sock *sk)
+{
+	if (sk->s_flags & NL_NO_AUTO_ACK)
+		return 0;
+	else
+		return nl_wait_for_ack(sk);
+}
+
 #endif
diff --git a/include/netlink-tc.h b/include/netlink-tc.h
index 65be588..71a20ff 100644
--- a/include/netlink-tc.h
+++ b/include/netlink-tc.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_TC_PRIV_H_
@@ -34,11 +34,10 @@
 extern int  tca_msg_parser(struct nlmsghdr *, struct rtnl_tca *);
 extern void tca_free_data(struct rtnl_tca *);
 extern int  tca_clone(struct rtnl_tca *, struct rtnl_tca *);
-extern int  tca_dump_brief(struct rtnl_tca *, const char *,
-			   struct nl_dump_params *, int);
-extern int  tca_dump_full(struct rtnl_tca *, struct nl_dump_params *, int);
-extern int  tca_dump_stats(struct rtnl_tca *,
-				struct nl_dump_params *, int);
+extern void tca_dump_line(struct rtnl_tca *, const char *,
+			  struct nl_dump_params *);
+extern void tca_dump_details(struct rtnl_tca *, struct nl_dump_params *);
+extern void tca_dump_stats(struct rtnl_tca *, struct nl_dump_params *);
 extern int  tca_compare(struct nl_object *, struct nl_object *, uint32_t, int);
 
 extern void tca_set_ifindex(struct rtnl_tca *, int);
@@ -51,7 +50,7 @@
 extern char *tca_get_kind(struct rtnl_tca *);
 extern uint64_t tca_get_stat(struct rtnl_tca *, int );
 
-extern struct nl_msg *tca_build_msg(struct rtnl_tca *tca, int type, int flags);
+extern int tca_build_msg(struct rtnl_tca *, int, int, struct nl_msg **);
 
 static inline void *tca_priv(struct rtnl_tca *tca)
 {
diff --git a/include/netlink-types.h b/include/netlink-types.h
index f7c6437..ff699bb 100644
--- a/include/netlink-types.h
+++ b/include/netlink-types.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_LOCAL_TYPES_H_
@@ -22,11 +22,12 @@
 #define NL_SOCK_PASSCRED	(1<<1)
 #define NL_OWN_PORT		(1<<2)
 #define NL_MSG_PEEK		(1<<3)
+#define NL_NO_AUTO_ACK		(1<<4)
 
 #define NL_MSG_CRED_PRESENT 1
 
 struct nl_cache_ops;
-struct nl_handle;
+struct nl_sock;
 struct nl_object;
 
 struct nl_cb
@@ -39,34 +40,34 @@
 
 	/** May be used to replace nl_recvmsgs with your own implementation
 	 * in all internal calls to nl_recvmsgs. */
-	int			(*cb_recvmsgs_ow)(struct nl_handle *,
+	int			(*cb_recvmsgs_ow)(struct nl_sock *,
 						  struct nl_cb *);
 
 	/** Overwrite internal calls to nl_recv, must return the number of
 	 * octets read and allocate a buffer for the received data. */
-	int			(*cb_recv_ow)(struct nl_handle *,
+	int			(*cb_recv_ow)(struct nl_sock *,
 					      struct sockaddr_nl *,
 					      unsigned char **,
 					      struct ucred **);
 
 	/** Overwrites internal calls to nl_send, must send the netlink
 	 * message. */
-	int			(*cb_send_ow)(struct nl_handle *,
+	int			(*cb_send_ow)(struct nl_sock *,
 					      struct nl_msg *);
 
 	int			cb_refcnt;
 };
 
-struct nl_handle
+struct nl_sock
 {
-	struct sockaddr_nl	h_local;
-	struct sockaddr_nl	h_peer;
-	int			h_fd;
-	int			h_proto;
-	unsigned int		h_seq_next;
-	unsigned int		h_seq_expect;
-	int			h_flags;
-	struct nl_cb *		h_cb;
+	struct sockaddr_nl	s_local;
+	struct sockaddr_nl	s_peer;
+	int			s_fd;
+	int			s_proto;
+	unsigned int		s_seq_next;
+	unsigned int		s_seq_expect;
+	int			s_flags;
+	struct nl_cb *		s_cb;
 };
 
 struct nl_cache
@@ -82,6 +83,7 @@
 {
 	struct nl_cache *	ca_cache;
 	change_func_t		ca_change;
+	void *			ca_change_data;
 };
 
 struct nl_cache_mngr
@@ -89,22 +91,13 @@
 	int			cm_protocol;
 	int			cm_flags;
 	int			cm_nassocs;
-	struct nl_handle *	cm_handle;
+	struct nl_sock *	cm_handle;
 	struct nl_cache_assoc *	cm_assocs;
 };
 
 struct nl_parser_param;
 
-struct genl_info
-{
-	struct sockaddr_nl *	who;
-	struct nlmsghdr *	nlh;
-	struct genlmsghdr *	genlhdr;
-	void *			userhdr;
-	struct nlattr **	attrs;
-};
-
-#define LOOSE_FLAG_COMPARISON	1
+#define LOOSE_COMPARISON	1
 
 #define NL_OBJ_MARK		1
 
@@ -138,6 +131,7 @@
 	struct ucred		nm_creds;
 	struct nlmsghdr *	nm_nlh;
 	size_t			nm_size;
+	int			nm_refcnt;
 };
 
 struct rtnl_link_map
@@ -244,11 +238,6 @@
 	uint32_t a_flag_mask;
 };
 
-#define NEXTHOP_HAS_FLAGS   0x000001
-#define NEXTHOP_HAS_WEIGHT  0x000002
-#define NEXTHOP_HAS_IFINDEX 0x000004
-#define NEXTHOP_HAS_GATEWAY 0x000008
-
 struct rtnl_nexthop
 {
 	uint8_t			rtnh_flags;
@@ -257,9 +246,9 @@
 	/* 1 byte spare */
 	uint32_t		rtnh_ifindex;
 	struct nl_addr *	rtnh_gateway;
-	uint32_t		rtnh_mask;
-
+	uint32_t		ce_mask; /* HACK to support attr macros */
 	struct nl_list_head	rtnh_list;
+	uint32_t		rtnh_realms;
 };
 
 struct rtnl_route
@@ -270,24 +259,22 @@
 	uint8_t			rt_dst_len;
 	uint8_t			rt_src_len;
 	uint8_t			rt_tos;
-	uint8_t			rt_table;
 	uint8_t			rt_protocol;
 	uint8_t			rt_scope;
 	uint8_t			rt_type;
+	uint8_t			rt_nmetrics;
 	uint32_t		rt_flags;
 	struct nl_addr *	rt_dst;
 	struct nl_addr *	rt_src;
-	char			rt_iif[IFNAMSIZ];
-	uint32_t		rt_oif;
-	struct nl_addr *	rt_gateway;
+	uint32_t		rt_table;
+	uint32_t		rt_iif;
 	uint32_t		rt_prio;
 	uint32_t		rt_metrics[RTAX_MAX];
 	uint32_t		rt_metrics_mask;
+	uint32_t		rt_nr_nh;
 	struct nl_addr *	rt_pref_src;
 	struct nl_list_head	rt_nexthops;
-	realm_t			rt_realms;
 	struct rtnl_rtcacheinfo	rt_cacheinfo;
-	uint32_t		rt_mp_algo;
 	uint32_t		rt_flag_mask;
 };
 
@@ -467,7 +454,7 @@
 	struct nl_data *	pre ##_opts;		\
 	uint64_t		pre ##_stats[RTNL_TC_STATS_MAX+1]; \
 	struct nl_data *	pre ##_xstats;		\
-	void *			pre ##_subdata;		\
+	struct nl_data *	pre ##_subdata;		\
 
 
 struct rtnl_tca
@@ -490,8 +477,8 @@
 struct rtnl_cls
 {
 	NL_TCA_GENERIC(c);
-	uint32_t	c_prio;
-	uint32_t	c_protocol;
+	uint16_t		c_prio;
+	uint16_t		c_protocol;
 	struct rtnl_cls_ops	*c_ops;
 };
 
@@ -509,6 +496,12 @@
 	int			cu_mask;
 };
 
+struct rtnl_cgroup
+{
+	struct rtnl_ematch_tree *cg_ematch;
+	int			cg_mask;
+};
+
 struct rtnl_fw
 {
 	uint32_t		cf_classid;
@@ -518,6 +511,26 @@
 	int			cf_mask;
 };
 
+struct rtnl_ematch
+{
+	uint16_t		e_id;
+	uint16_t		e_kind;
+	uint16_t		e_flags;
+
+	struct nl_list_head	e_childs;
+	struct nl_list_head	e_list;
+	struct rtnl_ematch_ops *e_ops;
+
+	char			e_data[0];
+};
+
+struct rtnl_ematch_tree
+{
+	uint16_t		et_progid;
+	struct nl_list_head	et_list;
+
+};
+
 struct rtnl_dsmark_qdisc
 {
 	uint16_t	qdm_indices;
@@ -582,6 +595,18 @@
 	uint32_t	nmro_correlation;
 };
 
+struct rtnl_netem_crpt
+{
+	uint32_t	nmcr_probability;
+	uint32_t	nmcr_correlation;
+};
+
+struct rtnl_netem_dist
+{
+	int16_t	*	dist_data;
+	size_t		dist_size;
+};
+
 struct rtnl_netem
 {
 	uint32_t		qnm_latency;
@@ -593,6 +618,8 @@
 	uint32_t		qnm_mask;
 	struct rtnl_netem_corr	qnm_corr;
 	struct rtnl_netem_reo	qnm_ro;
+	struct rtnl_netem_crpt	qnm_crpt;
+	struct rtnl_netem_dist  qnm_dist;
 };
 
 struct rtnl_htb_qdisc
@@ -736,23 +763,67 @@
 struct nfnl_log {
 	NLHDR_COMMON
 
-	uint8_t			log_family;
-	uint8_t			log_hook;
-	uint16_t		log_hwproto;
-	uint32_t		log_mark;
-	struct timeval		log_timestamp;
-	uint32_t		log_indev;
-	uint32_t		log_outdev;
-	uint32_t		log_physindev;
-	uint32_t		log_physoutdev;
-	uint8_t			log_hwaddr[8];
-	int			log_hwaddr_len;
-	void *			log_payload;
-	int			log_payload_len;
-	char *			log_prefix;
-	uint32_t		log_uid;
-	uint32_t		log_seq;
-	uint32_t		log_seq_global;
+	uint16_t		log_group;
+	uint8_t			log_copy_mode;
+	uint32_t		log_copy_range;
+	uint32_t		log_flush_timeout;
+	uint32_t		log_alloc_size;
+	uint32_t		log_queue_threshold;
+	uint32_t		log_flags;
+	uint32_t		log_flag_mask;
+};
+
+struct nfnl_log_msg {
+	NLHDR_COMMON
+
+	uint8_t			log_msg_family;
+	uint8_t			log_msg_hook;
+	uint16_t		log_msg_hwproto;
+	uint32_t		log_msg_mark;
+	struct timeval		log_msg_timestamp;
+	uint32_t		log_msg_indev;
+	uint32_t		log_msg_outdev;
+	uint32_t		log_msg_physindev;
+	uint32_t		log_msg_physoutdev;
+	uint8_t			log_msg_hwaddr[8];
+	int			log_msg_hwaddr_len;
+	void *			log_msg_payload;
+	int			log_msg_payload_len;
+	char *			log_msg_prefix;
+	uint32_t		log_msg_uid;
+	uint32_t		log_msg_gid;
+	uint32_t		log_msg_seq;
+	uint32_t		log_msg_seq_global;
+};
+
+struct nfnl_queue {
+	NLHDR_COMMON
+
+	uint16_t		queue_group;
+	uint32_t		queue_maxlen;
+	uint32_t		queue_copy_range;
+	uint8_t			queue_copy_mode;
+};
+
+struct nfnl_queue_msg {
+	NLHDR_COMMON
+
+	uint16_t		queue_msg_group;
+	uint8_t			queue_msg_family;
+	uint8_t			queue_msg_hook;
+	uint16_t		queue_msg_hwproto;
+	uint32_t		queue_msg_packetid;
+	uint32_t		queue_msg_mark;
+	struct timeval		queue_msg_timestamp;
+	uint32_t		queue_msg_indev;
+	uint32_t		queue_msg_outdev;
+	uint32_t		queue_msg_physindev;
+	uint32_t		queue_msg_physoutdev;
+	uint8_t			queue_msg_hwaddr[8];
+	int			queue_msg_hwaddr_len;
+	void *			queue_msg_payload;
+	int			queue_msg_payload_len;
+	uint32_t		queue_msg_verdict;
 };
 
 #endif
diff --git a/include/netlink/.gitignore b/include/netlink/.gitignore
new file mode 100644
index 0000000..6702033
--- /dev/null
+++ b/include/netlink/.gitignore
@@ -0,0 +1 @@
+version.h
diff --git a/include/netlink/addr.h b/include/netlink/addr.h
index 25fce7e..cc3d201 100644
--- a/include/netlink/addr.h
+++ b/include/netlink/addr.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_ADDR_H_
@@ -22,8 +22,9 @@
 
 /* Creation */
 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_parse(const char *, int);
+extern int		nl_addr_parse(const char *, int, struct nl_addr **);
 extern struct nl_addr *	nl_addr_clone(struct nl_addr *);
 
 /* Destroyage */
@@ -41,7 +42,7 @@
 extern int      	nl_addr_guess_family(struct nl_addr *);
 extern int		nl_addr_fill_sockaddr(struct nl_addr *,
 					      struct sockaddr *, socklen_t *);
-extern struct addrinfo *nl_addr_info(struct nl_addr *addr);
+extern int		nl_addr_info(struct nl_addr *, struct addrinfo **);
 extern int		nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen);
 
 /* Access Functions */
diff --git a/include/netlink/attr.h b/include/netlink/attr.h
index 6160f5f..8479c23 100644
--- a/include/netlink/attr.h
+++ b/include/netlink/attr.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_ATTR_H_
@@ -24,228 +24,224 @@
 struct nl_msg;
 
 /**
- * @name Validation Policy Types
+ * @name Basic Attribute Data Types
  * @{
  */
 
  /**
   * @ingroup attr
-  * Standard attribute types to specify validation policy
+  * Basic attribute data types
+  *
+  * See \ref attr_datatypes for more details.
   */
 enum {
-	NLA_UNSPEC,	/**< Unspecified type */
-	NLA_U8,		/**< 8bit integer */
-	NLA_U16,	/**< 16bit integer */
-	NLA_U32,	/**< 32bit integer */
-	NLA_U64,	/**< 64bit integer */
-	NLA_STRING,	/**< character string */
-	NLA_FLAG,	/**< flag */
-	NLA_MSECS,	/**< micro seconds (64bit) */
-	NLA_NESTED,	/**< nested attributes */
+	NLA_UNSPEC,	/**< Unspecified type, binary data chunk */
+	NLA_U8,		/**< 8 bit integer */
+	NLA_U16,	/**< 16 bit integer */
+	NLA_U32,	/**< 32 bit integer */
+	NLA_U64,	/**< 64 bit integer */
+	NLA_STRING,	/**< NUL terminated character string */
+	NLA_FLAG,	/**< Flag */
+	NLA_MSECS,	/**< Micro seconds (64bit) */
+	NLA_NESTED,	/**< Nested attributes */
 	__NLA_TYPE_MAX,
 };
 
-/**
- * @ingroup attr
- * Maximum netlink validation policy type
- */
 #define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
 
 /** @} */
 
 /**
  * @ingroup attr
- * attribute validation policy
+ * Attribute validation policy.
  *
- * Policies are defined as arrays of this struct, the array must
- * be accessible by attribute type up to the highest identifier
- * to be expected.
- *
- * Example:
- * @code
- * static struct nla_policy my_policy[ATTR_MAX+1] __read_mostly = {
- * 	[ATTR_FOO] = { .type = NLA_U16 },
- *	[ATTR_BAR] = { .type = NLA_STRING },
- *	[ATTR_BAZ] = { .minlen = sizeof(struct mystruct) },
- * };
- * @endcode
+ * See \ref attr_datatypes for more details.
  */
 struct nla_policy {
 	/** Type of attribute or NLA_UNSPEC */
 	uint16_t	type;
 
-	/** Minimal length of payload required to be available */
+	/** Minimal length of payload required */
 	uint16_t	minlen;
 
-	/** Maximal length of payload required to be available */
+	/** Maximal length of payload allowed */
 	uint16_t	maxlen;
 };
 
-/* size calculations */
+/* Size calculations */
 extern int		nla_attr_size(int payload);
 extern int		nla_total_size(int payload);
 extern int		nla_padlen(int payload);
 
-/* payload access */
+/* Attribute parsing */
 extern int		nla_type(const struct nlattr *);
 extern void *		nla_data(const struct nlattr *);
 extern int		nla_len(const struct nlattr *);
-
-/* attribute parsing */
 extern int		nla_ok(const struct nlattr *, int);
 extern struct nlattr *	nla_next(const struct nlattr *, int *);
 extern int		nla_parse(struct nlattr **, int, struct nlattr *,
 				  int, struct nla_policy *);
-extern int		nla_parse_nested(struct nlattr **, int, struct nlattr *,
-					 struct nla_policy *);
 extern int		nla_validate(struct nlattr *, int, int,
 				     struct nla_policy *);
 extern struct nlattr *	nla_find(struct nlattr *, int, int);
 
-/* utilities */
+/* Helper Functions */
 extern int		nla_memcpy(void *, struct nlattr *, int);
 extern size_t		nla_strlcpy(char *, const struct nlattr *, size_t);
 extern int		nla_memcmp(const struct nlattr *, const void *, size_t);
 extern int		nla_strcmp(const struct nlattr *, const char *);
 
-/* attribute construction */
+/* Unspecific attribute */
 extern struct nlattr *	nla_reserve(struct nl_msg *, int, int);
 extern int		nla_put(struct nl_msg *, int, int, const void *);
-extern int		nla_put_nested(struct nl_msg *, int, struct nl_msg *);
-extern int		nla_put_u8(struct nl_msg *, int, uint8_t);
-extern int		nla_put_u16(struct nl_msg *, int, uint16_t);
-extern int		nla_put_u32(struct nl_msg *, int, uint32_t);
-extern int		nla_put_u64(struct nl_msg *, int, uint64_t);
-extern int		nla_put_string(struct nl_msg *, int, const char *);
-extern int		nla_put_flag(struct nl_msg *, int);
-extern int		nla_put_msecs(struct nl_msg *, int, unsigned long);
 extern int		nla_put_data(struct nl_msg *, int, struct nl_data *);
 extern int		nla_put_addr(struct nl_msg *, int, struct nl_addr *);
 
-/* attribute nesting */
+/* Integer attribute */
+extern uint8_t		nla_get_u8(struct nlattr *);
+extern int		nla_put_u8(struct nl_msg *, int, uint8_t);
+extern uint16_t		nla_get_u16(struct nlattr *);
+extern int		nla_put_u16(struct nl_msg *, int, uint16_t);
+extern uint32_t		nla_get_u32(struct nlattr *);
+extern int		nla_put_u32(struct nl_msg *, int, uint32_t);
+extern uint64_t		nla_get_u64(struct nlattr *);
+extern int		nla_put_u64(struct nl_msg *, int, uint64_t);
+
+/* String attribute */
+extern char *		nla_get_string(struct nlattr *);
+extern char *		nla_strdup(struct nlattr *);
+extern int		nla_put_string(struct nl_msg *, int, const char *);
+
+/* Flag attribute */
+extern int		nla_get_flag(struct nlattr *);
+extern int		nla_put_flag(struct nl_msg *, int);
+
+/* Msec attribute */
+extern unsigned long	nla_get_msecs(struct nlattr *);
+extern int		nla_put_msecs(struct nl_msg *, int, unsigned long);
+
+/* Attribute nesting */
+extern int		nla_put_nested(struct nl_msg *, int, struct nl_msg *);
 extern struct nlattr *	nla_nest_start(struct nl_msg *, int);
 extern int		nla_nest_end(struct nl_msg *, struct nlattr *);
-
-/* attribute reading */
-extern uint8_t		nla_get_u8(struct nlattr *);
-extern uint16_t		nla_get_u16(struct nlattr *);
-extern uint32_t		nla_get_u32(struct nlattr *);
-extern uint64_t		nla_get_u64(struct nlattr *);
-extern char *		nla_get_string(struct nlattr *);
-extern int		nla_get_flag(struct nlattr *);
-extern unsigned long	nla_get_msecs(struct nlattr *);
-extern struct nl_data *	nla_get_data(struct nlattr *);
-extern struct nl_addr *	nla_get_addr(struct nlattr *, int);
+extern int		nla_parse_nested(struct nlattr **, int, struct nlattr *,
+					 struct nla_policy *);
 
 /**
  * @name Attribute Construction (Exception Based)
- *
- * All these functions jump to nla_put_failure in case of a failure
- * instead of returning an error code.
- * 
  * @{
  */
 
 /**
  * @ingroup attr
- * Add a netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg attrlen		length of attribute payload
- * @arg data		head of attribute payload
+ * Add unspecific attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg attrlen		Length of attribute payload.
+ * @arg data		Head of attribute payload.
  */
-#define NLA_PUT(n, attrtype, attrlen, data) \
+#define NLA_PUT(msg, attrtype, attrlen, data) \
 	do { \
-		if (nla_put(n, attrtype, attrlen, data) < 0) \
+		if (nla_put(msg, attrtype, attrlen, data) < 0) \
 			goto nla_put_failure; \
 	} while(0)
 
 /**
  * @ingroup attr
- * Add a basic netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg type		atomic type
- * @arg attrtype	attribute type
- * @arg value		head of attribute payload
+ * Add atomic type attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg type		Atomic type.
+ * @arg attrtype	Attribute type.
+ * @arg value		Head of attribute payload.
  */
-#define NLA_PUT_TYPE(n, type, attrtype, value) \
+#define NLA_PUT_TYPE(msg, type, attrtype, value) \
 	do { \
 		type __tmp = value; \
-		NLA_PUT(n, attrtype, sizeof(type), &__tmp); \
+		NLA_PUT(msg, attrtype, sizeof(type), &__tmp); \
 	} while(0)
 
 /**
- * Add a u8 netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg value		numeric value
+ * Add 8 bit integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value.
  */
-#define NLA_PUT_U8(n, attrtype, value) \
-	NLA_PUT_TYPE(n, uint8_t, attrtype, value)
+#define NLA_PUT_U8(msg, attrtype, value) \
+	NLA_PUT_TYPE(msg, uint8_t, attrtype, value)
 
 /**
- * Add a u16 netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg value		numeric value
+ * Add 16 bit integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value.
  */
-#define NLA_PUT_U16(n, attrtype, value) \
-	NLA_PUT_TYPE(n, uint16_t, attrtype, value)
+#define NLA_PUT_U16(msg, attrtype, value) \
+	NLA_PUT_TYPE(msg, uint16_t, attrtype, value)
 
 /**
- * Add a u32 netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg value		numeric value
+ * Add 32 bit integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value.
  */
-#define NLA_PUT_U32(n, attrtype, value) \
-	NLA_PUT_TYPE(n, uint32_t, attrtype, value)
+#define NLA_PUT_U32(msg, attrtype, value) \
+	NLA_PUT_TYPE(msg, uint32_t, attrtype, value)
 
 /**
- * Add a u64 netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg value		numeric value
+ * Add 64 bit integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value.
  */
-#define NLA_PUT_U64(n, attrtype, value) \
-	NLA_PUT_TYPE(n, uint64_t, attrtype, value)
+#define NLA_PUT_U64(msg, attrtype, value) \
+	NLA_PUT_TYPE(msg, uint64_t, attrtype, value)
 
 /**
- * Add a character string netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg value		character string
+ * Add string attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		NUL terminated character string.
  */
-#define NLA_PUT_STRING(n, attrtype, value) \
-	NLA_PUT(n, attrtype, strlen(value) + 1, value)
+#define NLA_PUT_STRING(msg, attrtype, value) \
+	NLA_PUT(msg, attrtype, strlen(value) + 1, value)
 
 /**
- * Add a flag netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
+ * Add flag attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
  */
-#define NLA_PUT_FLAG(n, attrtype) \
-	NLA_PUT(n, attrtype, 0, NULL)
+#define NLA_PUT_FLAG(msg, attrtype) \
+	NLA_PUT(msg, attrtype, 0, NULL)
 
 /**
- * Add a msecs netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg msecs		numeric value in micro seconds
+ * Add msecs attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg msecs		Numeric value in micro seconds.
  */
-#define NLA_PUT_MSECS(n, attrtype, msecs) \
-	NLA_PUT_U64(n, attrtype, msecs)
+#define NLA_PUT_MSECS(msg, attrtype, msecs) \
+	NLA_PUT_U64(msg, attrtype, msecs)
 
 /**
- * Add a address attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg addr		abstract address object
+ * Add address attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg addr		Abstract address object.
  */
-#define NLA_PUT_ADDR(n, attrtype, addr) \
-	NLA_PUT(n, attrtype, nl_addr_get_len(addr), \
+#define NLA_PUT_ADDR(msg, attrtype, addr) \
+	NLA_PUT(msg, attrtype, nl_addr_get_len(addr), \
 		nl_addr_get_binary_addr(addr))
 
+/**
+ * Add abstract data attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg data		Abstract data object.
+ */
+#define NLA_PUT_DATA(msg, attrtype, data) \
+	NLA_PUT(msg, attrtype, nl_data_get_size(data), \
+		nl_data_get(data))
+
 /** @} */
 
 /**
@@ -255,7 +251,7 @@
 
 /**
  * @ingroup attr
- * iterate over a stream of attributes
+ * Iterate over a stream of attributes
  * @arg pos	loop counter, set to current attribute
  * @arg head	head of attribute stream
  * @arg len	length of attribute stream
@@ -268,7 +264,7 @@
 
 /**
  * @ingroup attr
- * iterate over a stream of nested attributes
+ * Iterate over a stream of nested attributes
  * @arg pos	loop counter, set to current attribute
  * @arg nla	attribute containing the nested attributes
  * @arg rem	initialized to len, holds bytes currently remaining in stream
diff --git a/include/netlink/cache-api.h b/include/netlink/cache-api.h
index 96699ed..22fc449 100644
--- a/include/netlink/cache-api.h
+++ b/include/netlink/cache-api.h
@@ -39,7 +39,7 @@
  * // function which must trigger a complete dump on the kernel-side of
  * // whatever the cache covers.
  * static int my_request_update(struct nl_cache *cache,
- * 				struct nl_handle *socket)
+ * 				struct nl_sock *socket)
  * {
  * 	// In this example, we request a full dump of the interface table
  * 	return nl_rtgen_request(socket, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP);
@@ -172,7 +172,7 @@
 	 * Called whenever an update of the cache is required. Must send
 	 * a request message to the kernel requesting a complete dump.
 	 */
-	int   (*co_request_update)(struct nl_cache *, struct nl_handle *);
+	int   (*co_request_update)(struct nl_cache *, struct nl_sock *);
 
 	/**
 	 * Called whenever a message was received that needs to be parsed.
diff --git a/include/netlink/cache.h b/include/netlink/cache.h
index cb7741b..c752920 100644
--- a/include/netlink/cache.h
+++ b/include/netlink/cache.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_CACHE_H_
@@ -24,7 +24,7 @@
 
 struct nl_cache;
 
-typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int);
+typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int, void *);
 
 /* Access Functions */
 extern int			nl_cache_nitems(struct nl_cache *);
@@ -36,10 +36,12 @@
 extern struct nl_object *	nl_cache_get_next(struct nl_object *);
 extern struct nl_object *	nl_cache_get_prev(struct nl_object *);
 
-/* Cache creation/deletion */
-#define nl_cache_alloc_from_ops(ptr)	nl_cache_alloc(ptr)
 extern struct nl_cache *	nl_cache_alloc(struct nl_cache_ops *);
-extern struct nl_cache *	nl_cache_alloc_name(const char *);
+extern int			nl_cache_alloc_and_fill(struct nl_cache_ops *,
+							struct nl_sock *,
+							struct nl_cache **);
+extern int			nl_cache_alloc_name(const char *,
+						    struct nl_cache **);
 extern struct nl_cache *	nl_cache_subset(struct nl_cache *,
 						struct nl_object *);
 extern void			nl_cache_clear(struct nl_cache *);
@@ -50,19 +52,19 @@
 					     struct nl_object *);
 extern int			nl_cache_parse_and_add(struct nl_cache *,
 						       struct nl_msg *);
-#define nl_cache_delete(a, b)	nl_cache_remove(b)
 extern void			nl_cache_remove(struct nl_object *);
-#define nl_cache_update(a, b)	nl_cache_refill(a, b)
-extern int			nl_cache_refill(struct nl_handle *,
+extern int			nl_cache_refill(struct nl_sock *,
 						struct nl_cache *);
-extern int			nl_cache_pickup(struct nl_handle *,
+extern int			nl_cache_pickup(struct nl_sock *,
 						struct nl_cache *);
-extern int			nl_cache_resync(struct nl_handle *,
+extern int			nl_cache_resync(struct nl_sock *,
 						struct nl_cache *,
-						change_func_t);
+						change_func_t,
+						void *);
 extern int			nl_cache_include(struct nl_cache *,
 						 struct nl_object *,
-						 change_func_t);
+						 change_func_t,
+						 void *);
 
 /* General */
 extern int			nl_cache_is_empty(struct nl_cache *);
@@ -106,11 +108,14 @@
 
 #define NL_AUTO_PROVIDE		1
 
-extern struct nl_cache_mngr *	nl_cache_mngr_alloc(struct nl_handle *,
-						    int, int);
-extern struct nl_cache *	nl_cache_mngr_add(struct nl_cache_mngr *,
+extern int			nl_cache_mngr_alloc(struct nl_sock *,
+						    int, int,
+						    struct nl_cache_mngr **);
+extern int			nl_cache_mngr_add(struct nl_cache_mngr *,
 						  const char *,
-						  change_func_t);
+						  change_func_t,
+						  void *,
+						  struct nl_cache **);
 extern int			nl_cache_mngr_get_fd(struct nl_cache_mngr *);
 extern int			nl_cache_mngr_poll(struct nl_cache_mngr *,
 						   int);
diff --git a/include/netlink/cli/addr.h b/include/netlink/cli/addr.h
new file mode 100644
index 0000000..d0fd055
--- /dev/null
+++ b/include/netlink/cli/addr.h
@@ -0,0 +1,32 @@
+/*
+ * netlink/cli/addr.h    CLI Address Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_ADDR_H_
+#define __NETLINK_CLI_ADDR_H_
+
+#include <netlink/route/addr.h>
+
+#define nl_cli_addr_alloc_cache(sk) \
+		nl_cli_alloc_cache((sk), "address", rtnl_addr_alloc_cache)
+
+extern struct rtnl_addr *nl_cli_addr_alloc(void);
+
+extern void nl_cli_addr_parse_family(struct rtnl_addr *, char *);
+extern void nl_cli_addr_parse_local(struct rtnl_addr *, char *);
+extern void nl_cli_addr_parse_dev(struct rtnl_addr *, struct nl_cache *,char *);
+extern void nl_cli_addr_parse_label(struct rtnl_addr *, char *);
+extern void nl_cli_addr_parse_peer(struct rtnl_addr *, char *);
+extern void nl_cli_addr_parse_scope(struct rtnl_addr *, char *);
+extern void nl_cli_addr_parse_broadcast(struct rtnl_addr *, char *);
+extern void nl_cli_addr_parse_preferred(struct rtnl_addr *, char *);
+extern void nl_cli_addr_parse_valid(struct rtnl_addr *, char *);
+
+#endif
diff --git a/include/netlink/cli/ct.h b/include/netlink/cli/ct.h
new file mode 100644
index 0000000..bed776b
--- /dev/null
+++ b/include/netlink/cli/ct.h
@@ -0,0 +1,34 @@
+/*
+ * netlink/cli/ct.h	CLI Conntrack Helper
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_CT_H_
+#define __NETLINK_CLI_CT_H_
+
+#include <netlink/netfilter/ct.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+
+extern struct nfnl_ct *nl_cli_ct_alloc(void);
+extern struct nl_cache *nl_cli_ct_alloc_cache(struct nl_sock *);
+
+extern void nl_cli_ct_parse_family(struct nfnl_ct *, char *);
+extern void nl_cli_ct_parse_protocol(struct nfnl_ct *, char *);
+extern void nl_cli_ct_parse_mark(struct nfnl_ct *, char *);
+extern void nl_cli_ct_parse_timeout(struct nfnl_ct *, char *);
+extern void nl_cli_ct_parse_id(struct nfnl_ct *, char *);
+extern void nl_cli_ct_parse_use(struct nfnl_ct *, char *);
+extern void nl_cli_ct_parse_src(struct nfnl_ct *, int, char *);
+extern void nl_cli_ct_parse_dst(struct nfnl_ct *, int, char *);
+extern void nl_cli_ct_parse_src_port(struct nfnl_ct *, int, char *);
+extern void nl_cli_ct_parse_dst_port(struct nfnl_ct *, int, char *);
+extern void nl_cli_ct_parse_tcp_state(struct nfnl_ct *, char *);
+extern void nl_cli_ct_parse_status(struct nfnl_ct *, char *);
+
+#endif
diff --git a/include/netlink/cli/link.h b/include/netlink/cli/link.h
new file mode 100644
index 0000000..c404019
--- /dev/null
+++ b/include/netlink/cli/link.h
@@ -0,0 +1,30 @@
+/*
+ * netlink/cli/link.h     CLI Link Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_LINK_H_
+#define __NETLINK_CLI_LINK_H_
+
+#include <netlink/route/link.h>
+#include <netlink/cli/utils.h>
+
+#define nl_cli_link_alloc_cache(sk) \
+		nl_cli_alloc_cache((sk), "link", rtnl_link_alloc_cache)
+
+extern struct rtnl_link *nl_cli_link_alloc(void);
+
+extern void nl_cli_link_parse_family(struct rtnl_link *, char *);
+extern void nl_cli_link_parse_name(struct rtnl_link *, char *);
+extern void nl_cli_link_parse_mtu(struct rtnl_link *, char *);
+extern void nl_cli_link_parse_ifindex(struct rtnl_link *, char *);
+extern void nl_cli_link_parse_txqlen(struct rtnl_link *, char *);
+extern void nl_cli_link_parse_weight(struct rtnl_link *, char *);
+
+#endif
diff --git a/include/netlink/cli/neigh.h b/include/netlink/cli/neigh.h
new file mode 100644
index 0000000..5440012
--- /dev/null
+++ b/include/netlink/cli/neigh.h
@@ -0,0 +1,27 @@
+/*
+ * netlink/cli/neighbour.h     CLI Neighbour Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_NEIGH_H_
+#define __NETLINK_CLI_NEIGH_H_
+
+#include <netlink/route/neighbour.h>
+
+#define nl_cli_neigh_alloc_cache(sk) \
+		nl_cli_alloc_cache((sk), "neighbour", rtnl_neigh_alloc_cache)
+
+extern struct rtnl_neigh *nl_cli_neigh_alloc(void);
+extern void nl_cli_neigh_parse_dst(struct rtnl_neigh *, char *);
+extern void nl_cli_neigh_parse_lladdr(struct rtnl_neigh *, char *);
+extern void nl_cli_neigh_parse_dev(struct rtnl_neigh *, struct nl_cache *, char *);
+extern void nl_cli_neigh_parse_family(struct rtnl_neigh *, char *);
+extern void nl_cli_neigh_parse_state(struct rtnl_neigh *, char *);
+
+#endif
diff --git a/include/netlink/cli/qdisc.h b/include/netlink/cli/qdisc.h
new file mode 100644
index 0000000..9fc4506
--- /dev/null
+++ b/include/netlink/cli/qdisc.h
@@ -0,0 +1,28 @@
+/*
+ * netlink/cli/qdisc.h     CLI QDisc Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_QDISC_H_
+#define __NETLINK_CLI_QDISC_H_
+
+#include <netlink/route/qdisc.h>
+
+#define nl_cli_qdisc_alloc_cache(sk) \
+		nl_cli_alloc_cache((sk), "queueing disciplines", \
+				   rtnl_qdisc_alloc_cache)
+
+extern struct rtnl_qdisc *nl_cli_qdisc_alloc(void);
+
+extern void nl_cli_qdisc_parse_dev(struct rtnl_qdisc *, struct nl_cache *, char *);
+extern void nl_cli_qdisc_parse_parent(struct rtnl_qdisc *, char *);
+extern void nl_cli_qdisc_parse_handle(struct rtnl_qdisc *, char *);
+extern void nl_cli_qdisc_parse_kind(struct rtnl_qdisc *, char *);
+
+#endif
diff --git a/include/netlink/cli/route.h b/include/netlink/cli/route.h
new file mode 100644
index 0000000..089c658
--- /dev/null
+++ b/include/netlink/cli/route.h
@@ -0,0 +1,34 @@
+/*
+ * netlink/cli//route.h     CLI Route Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_ROUTE_H_
+#define __NETLINK_CLI_ROUTE_H_
+
+#include <netlink/route/route.h>
+
+extern struct rtnl_route *nl_cli_route_alloc(void);
+
+extern struct nl_cache *nl_cli_route_alloc_cache(struct nl_sock *, int);
+
+extern void	nl_cli_route_parse_family(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_dst(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_src(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_pref_src(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_metric(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_nexthop(struct rtnl_route *, char *, struct nl_cache *);
+extern void	nl_cli_route_parse_table(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_prio(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_scope(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_protocol(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_type(struct rtnl_route *, char *);
+extern void	nl_cli_route_parse_iif(struct rtnl_route *, char *, struct nl_cache *);
+
+#endif
diff --git a/include/netlink/cli/rule.h b/include/netlink/cli/rule.h
new file mode 100644
index 0000000..61cd63e
--- /dev/null
+++ b/include/netlink/cli/rule.h
@@ -0,0 +1,21 @@
+/*
+ * netlink/cli/rule.h     CLI Routing Rule Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_RULE_H_
+#define __NETLINK_CLI_RULE_H_
+
+#include <netlink/route/rule.h>
+
+extern struct rtnl_rule *nl_cli_rule_alloc(void);
+extern struct nl_cache *nl_cli_rule_alloc_cache(struct nl_sock *);
+extern void nl_cli_rule_parse_family(struct rtnl_rule *, char *);
+
+#endif
diff --git a/include/netlink/cli/utils.h b/include/netlink/cli/utils.h
new file mode 100644
index 0000000..2a23208
--- /dev/null
+++ b/include/netlink/cli/utils.h
@@ -0,0 +1,80 @@
+/*
+ * src/utils.h		Utilities
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_UTILS_H_
+#define __NETLINK_CLI_UTILS_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/addr.h>
+#include <netlink/list.h>
+#include <netlink/route/rtnl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/addr.h>
+#include <netlink/route/neighbour.h>
+#include <netlink/route/neightbl.h>
+#include <netlink/route/route.h>
+#include <netlink/route/rule.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/class.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/cls/ematch.h>
+#include <netlink/fib_lookup/lookup.h>
+#include <netlink/fib_lookup/request.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/mngt.h>
+#include <netlink/netfilter/ct.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __init
+#define __init __attribute__((constructor))
+#endif
+
+#ifndef __exit
+#define __exit __attribute__((destructor))
+#endif
+
+extern uint32_t		nl_cli_parse_u32(const char *);
+extern void		nl_cli_print_version(void);
+extern void		nl_cli_fatal(int, const char *, ...);
+extern struct nl_addr *	nl_cli_addr_parse(const char *, int);
+extern int		nl_cli_connect(struct nl_sock *, int);
+extern struct nl_sock *	nl_cli_alloc_socket(void);
+extern int		nl_cli_parse_dumptype(const char *);
+extern int		nl_cli_confirm(struct nl_object *,
+				       struct nl_dump_params *, int);
+
+extern struct nl_cache *nl_cli_alloc_cache(struct nl_sock *, const char *,
+			     int (*ac)(struct nl_sock *, struct nl_cache **));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/data.h b/include/netlink/data.h
index bb28d13..071159e 100644
--- a/include/netlink/data.h
+++ b/include/netlink/data.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_DATA_H_
@@ -22,6 +22,7 @@
 
 /* General */
 extern struct nl_data *	nl_data_alloc(void *, size_t);
+extern struct nl_data * nl_data_alloc_attr(struct nlattr *);
 extern struct nl_data *	nl_data_clone(struct nl_data *);
 extern int		nl_data_append(struct nl_data *, void *, size_t);
 extern void		nl_data_free(struct nl_data *);
diff --git a/include/netlink/errno.h b/include/netlink/errno.h
new file mode 100644
index 0000000..c8a376e
--- /dev/null
+++ b/include/netlink/errno.h
@@ -0,0 +1,60 @@
+/*
+ * netlink/errno.h		Error Numbers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_ERRNO_H_
+#define NETLINK_ERRNO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NLE_SUCCESS		0
+#define NLE_FAILURE		1
+#define NLE_INTR		2
+#define NLE_BAD_SOCK		3
+#define NLE_AGAIN		4
+#define NLE_NOMEM		5
+#define NLE_EXIST		6
+#define NLE_INVAL		7
+#define NLE_RANGE		8
+#define NLE_MSGSIZE		9
+#define NLE_OPNOTSUPP		10
+#define NLE_AF_NOSUPPORT	11
+#define NLE_OBJ_NOTFOUND	12
+#define NLE_NOATTR		13
+#define NLE_MISSING_ATTR	14
+#define NLE_AF_MISMATCH		15
+#define NLE_SEQ_MISMATCH	16
+#define NLE_MSG_OVERFLOW	17
+#define NLE_MSG_TRUNC		18
+#define NLE_NOADDR		19
+#define NLE_SRCRT_NOSUPPORT	20
+#define NLE_MSG_TOOSHORT	21
+#define NLE_MSGTYPE_NOSUPPORT	22
+#define NLE_OBJ_MISMATCH	23
+#define NLE_NOCACHE		24
+#define NLE_BUSY		25
+#define NLE_PROTO_MISMATCH	26
+#define NLE_NOACCESS		27
+#define NLE_PERM		28
+#define NLE_PKTLOC_FILE		29
+
+#define NLE_MAX			NLE_PKTLOC_FILE
+
+extern const char *	nl_geterror(int);
+extern void		nl_perror(int, const char *);
+extern int		nl_syserr2nlerr(int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/fib_lookup/lookup.h b/include/netlink/fib_lookup/lookup.h
index 29c7ee8..8bf27b8 100644
--- a/include/netlink/fib_lookup/lookup.h
+++ b/include/netlink/fib_lookup/lookup.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_FIB_LOOKUP_H_
@@ -28,9 +28,10 @@
 
 extern struct nl_cache *	flnl_result_alloc_cache(void);
 
-extern struct nl_msg *		flnl_lookup_build_request(struct flnl_request *,
-							  int);
-extern int			flnl_lookup(struct nl_handle *,
+extern int			flnl_lookup_build_request(struct flnl_request *,
+							  int,
+							  struct nl_msg **);
+extern int			flnl_lookup(struct nl_sock *,
 					    struct flnl_request *,
 					    struct nl_cache *);
 
diff --git a/include/netlink/genl/ctrl.h b/include/netlink/genl/ctrl.h
index 5d65c68..1ae62f4 100644
--- a/include/netlink/genl/ctrl.h
+++ b/include/netlink/genl/ctrl.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_GENL_CTRL_H_
@@ -22,11 +22,12 @@
 
 struct genl_family;
 
-extern struct nl_cache *	genl_ctrl_alloc_cache(struct nl_handle *);
+extern int			genl_ctrl_alloc_cache(struct nl_sock *,
+						      struct nl_cache **);
 extern struct genl_family *	genl_ctrl_search(struct nl_cache *, int);
 extern struct genl_family *	genl_ctrl_search_by_name(struct nl_cache *,
 							 const char *);
-extern int			genl_ctrl_resolve(struct nl_handle *,
+extern int			genl_ctrl_resolve(struct nl_sock *,
 						  const char *);
 
 #ifdef __cplusplus
diff --git a/include/netlink/genl/genl.h b/include/netlink/genl/genl.h
index de142d0..3f3340c 100644
--- a/include/netlink/genl/genl.h
+++ b/include/netlink/genl/genl.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_GENL_H_
@@ -20,9 +20,9 @@
 extern "C" {
 #endif
 
-extern int		genl_connect(struct nl_handle *);
+extern int		genl_connect(struct nl_sock *);
 
-extern int		genl_send_simple(struct nl_handle *, int, int,
+extern int		genl_send_simple(struct nl_sock *, int, int,
 					 int, int);
 
 extern void *		genlmsg_put(struct nl_msg *, uint32_t, uint32_t,
diff --git a/include/netlink/genl/mngt.h b/include/netlink/genl/mngt.h
index 467922a..8b0244f 100644
--- a/include/netlink/genl/mngt.h
+++ b/include/netlink/genl/mngt.h
@@ -22,6 +22,15 @@
 
 struct nl_cache_ops;
 
+struct genl_info
+{
+	struct sockaddr_nl *    who;
+	struct nlmsghdr *       nlh;
+	struct genlmsghdr *     genlhdr;
+	void *                  userhdr;
+	struct nlattr **        attrs;
+};
+
 /**
  * @ingroup genl_mngt
  * Generic Netlink Command
diff --git a/include/netlink/handlers.h b/include/netlink/handlers.h
index fcd3e48..f373f58 100644
--- a/include/netlink/handlers.h
+++ b/include/netlink/handlers.h
@@ -23,8 +23,9 @@
 #endif
 
 struct nl_cb;
-struct nl_handle;
+struct nl_sock;
 struct nl_msg;
+struct ucred;
 
 /**
  * @name Callback Typedefs
@@ -64,10 +65,6 @@
 	NL_STOP,
 };
 
-/* backwards compatibility */
-#define NL_PROCEED NL_OK
-#define NL_EXIT NL_STOP
-
 /**
  * Callback kinds
  * @ingroup cb
@@ -129,15 +126,15 @@
 		      void *);
 
 extern void nl_cb_overwrite_recvmsgs(struct nl_cb *,
-				     int (*func)(struct nl_handle *,
+				     int (*func)(struct nl_sock *,
 						 struct nl_cb *));
 extern void nl_cb_overwrite_recv(struct nl_cb *,
-				 int (*func)(struct nl_handle *,
+				 int (*func)(struct nl_sock *,
 					     struct sockaddr_nl *,
 					     unsigned char **,
 					     struct ucred **));
 extern void nl_cb_overwrite_send(struct nl_cb *,
-				 int (*func)(struct nl_handle *,
+				 int (*func)(struct nl_sock *,
 					     struct nl_msg *));
 
 #ifdef __cplusplus
diff --git a/include/netlink/list.h b/include/netlink/list.h
index e7a2646..28712ed 100644
--- a/include/netlink/list.h
+++ b/include/netlink/list.h
@@ -18,6 +18,11 @@
 	struct nl_list_head *	prev;
 };
 
+static inline void NL_INIT_LIST_HEAD(struct nl_list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
 
 static inline void __nl_list_add(struct nl_list_head *obj,
 				 struct nl_list_head *prev,
@@ -68,6 +73,9 @@
 #define NL_LIST_HEAD(name) \
 	struct nl_list_head name = { &(name), &(name) }
 
+#define nl_list_first_entry(head, type, member)			\
+	nl_list_entry((head)->next, type, member)
+
 #define nl_list_for_each_entry(pos, head, member)				\
 	for (pos = nl_list_entry((head)->next, typeof(*pos), member);	\
 	     &(pos)->member != (head); 	\
diff --git a/include/netlink/msg.h b/include/netlink/msg.h
index 732e66f..e331f42 100644
--- a/include/netlink/msg.h
+++ b/include/netlink/msg.h
@@ -68,11 +68,6 @@
 extern int		  nlmsg_validate(struct nlmsghdr *, int, int,
 					 struct nla_policy *);
 
-/* Backward compatibility */
-#define nlmsg_new()	   		nlmsg_alloc()
-#define nlmsg_build_simple(a, b)	nlmsg_alloc_simple(a, b)
-#define nlmsg_build(ptr)		nlmsg_inherit(ptr)
-
 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);
@@ -86,6 +81,7 @@
 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 */
diff --git a/include/netlink/netfilter/ct.h b/include/netlink/netfilter/ct.h
index 965b869..c4402b3 100644
--- a/include/netlink/netfilter/ct.h
+++ b/include/netlink/netfilter/ct.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
  */
@@ -27,86 +27,97 @@
 
 extern struct nl_object_ops ct_obj_ops;
 
-/* General */
 extern struct nfnl_ct *	nfnl_ct_alloc(void);
-extern struct nl_cache *nfnl_ct_alloc_cache(struct nl_handle *);
+extern int	nfnl_ct_alloc_cache(struct nl_sock *, struct nl_cache **);
 
-extern int		nfnlmsg_ct_group(struct nlmsghdr *);
-extern struct nfnl_ct *	nfnlmsg_ct_parse(struct nlmsghdr *);
+extern int	nfnlmsg_ct_group(struct nlmsghdr *);
+extern int	nfnlmsg_ct_parse(struct nlmsghdr *, struct nfnl_ct **);
 
-extern void		nfnl_ct_get(struct nfnl_ct *);
-extern void		nfnl_ct_put(struct nfnl_ct *);
+extern void	nfnl_ct_get(struct nfnl_ct *);
+extern void	nfnl_ct_put(struct nfnl_ct *);
 
-extern int		nfnl_ct_dump_request(struct nl_handle *);
+extern int	nfnl_ct_dump_request(struct nl_sock *);
 
-extern void		nfnl_ct_set_family(struct nfnl_ct *, uint8_t);
-extern uint8_t		nfnl_ct_get_family(const struct nfnl_ct *);
+extern int	nfnl_ct_build_add_request(const struct nfnl_ct *, int,
+					  struct nl_msg **);
+extern int	nfnl_ct_add(struct nl_sock *, const struct nfnl_ct *, int);
 
-extern void		nfnl_ct_set_proto(struct nfnl_ct *, uint8_t);
-extern int		nfnl_ct_test_proto(const struct nfnl_ct *);
-extern uint8_t		nfnl_ct_get_proto(const struct nfnl_ct *);
+extern int	nfnl_ct_build_delete_request(const struct nfnl_ct *, int,
+					     struct nl_msg **);
+extern int	nfnl_ct_delete(struct nl_sock *, const struct nfnl_ct *, int);
 
-extern void		nfnl_ct_set_tcp_state(struct nfnl_ct *, uint8_t);
-extern int		nfnl_ct_test_tcp_state(const struct nfnl_ct *);
-extern uint8_t		nfnl_ct_get_tcp_state(const struct nfnl_ct *);
-extern char *		nfnl_ct_tcp_state2str(uint8_t, char *, size_t);
-extern int		nfnl_ct_str2tcp_state(const char *name);
+extern int	nfnl_ct_build_query_request(const struct nfnl_ct *, int,
+					    struct nl_msg **);
+extern int	nfnl_ct_query(struct nl_sock *, const struct nfnl_ct *, int);
 
-extern void		nfnl_ct_set_status(struct nfnl_ct *, uint32_t);
-extern void		nfnl_ct_unset_status(struct nfnl_ct *, uint32_t);
-extern uint32_t		nfnl_ct_get_status(const struct nfnl_ct *);
+extern void	nfnl_ct_set_family(struct nfnl_ct *, uint8_t);
+extern uint8_t	nfnl_ct_get_family(const struct nfnl_ct *);
 
-extern void		nfnl_ct_set_timeout(struct nfnl_ct *, uint32_t);
-extern int		nfnl_ct_test_timeout(const struct nfnl_ct *);
-extern uint32_t		nfnl_ct_get_timeout(const struct nfnl_ct *);
+extern void	nfnl_ct_set_proto(struct nfnl_ct *, uint8_t);
+extern int	nfnl_ct_test_proto(const struct nfnl_ct *);
+extern uint8_t	nfnl_ct_get_proto(const struct nfnl_ct *);
 
-extern void		nfnl_ct_set_mark(struct nfnl_ct *, uint32_t);
-extern int		nfnl_ct_test_mark(const struct nfnl_ct *);
-extern uint32_t		nfnl_ct_get_mark(const struct nfnl_ct *);
+extern void	nfnl_ct_set_tcp_state(struct nfnl_ct *, uint8_t);
+extern int	nfnl_ct_test_tcp_state(const struct nfnl_ct *);
+extern uint8_t	nfnl_ct_get_tcp_state(const struct nfnl_ct *);
+extern char *	nfnl_ct_tcp_state2str(uint8_t, char *, size_t);
+extern int	nfnl_ct_str2tcp_state(const char *name);
 
-extern void		nfnl_ct_set_use(struct nfnl_ct *, uint32_t);
-extern int		nfnl_ct_test_use(const struct nfnl_ct *);
-extern uint32_t		nfnl_ct_get_use(const struct nfnl_ct *);
+extern void	nfnl_ct_set_status(struct nfnl_ct *, uint32_t);
+extern void	nfnl_ct_unset_status(struct nfnl_ct *, uint32_t);
+extern uint32_t	nfnl_ct_get_status(const struct nfnl_ct *);
+extern char *	nfnl_ct_status2str(int, char *, size_t);
+extern int	nfnl_ct_str2status(const char *);
 
-extern void		nfnl_ct_set_id(struct nfnl_ct *, uint32_t);
-extern int		nfnl_ct_test_id(const struct nfnl_ct *);
-extern uint32_t		nfnl_ct_get_id(const struct nfnl_ct *);
+extern void	nfnl_ct_set_timeout(struct nfnl_ct *, uint32_t);
+extern int	nfnl_ct_test_timeout(const struct nfnl_ct *);
+extern uint32_t	nfnl_ct_get_timeout(const struct nfnl_ct *);
 
-extern int		nfnl_ct_set_src(struct nfnl_ct *, int,
-					struct nl_addr *);
+extern void	nfnl_ct_set_mark(struct nfnl_ct *, uint32_t);
+extern int	nfnl_ct_test_mark(const struct nfnl_ct *);
+extern uint32_t	nfnl_ct_get_mark(const struct nfnl_ct *);
+
+extern void	nfnl_ct_set_use(struct nfnl_ct *, uint32_t);
+extern int	nfnl_ct_test_use(const struct nfnl_ct *);
+extern uint32_t	nfnl_ct_get_use(const struct nfnl_ct *);
+
+extern void	nfnl_ct_set_id(struct nfnl_ct *, uint32_t);
+extern int	nfnl_ct_test_id(const struct nfnl_ct *);
+extern uint32_t	nfnl_ct_get_id(const struct nfnl_ct *);
+
+extern int	nfnl_ct_set_src(struct nfnl_ct *, int, struct nl_addr *);
 extern struct nl_addr *	nfnl_ct_get_src(const struct nfnl_ct *, int);
 
-extern int		nfnl_ct_set_dst(struct nfnl_ct *, int,
-					struct nl_addr *);
+extern int	nfnl_ct_set_dst(struct nfnl_ct *, int, struct nl_addr *);
 extern struct nl_addr *	nfnl_ct_get_dst(const struct nfnl_ct *, int);
 
-extern void		nfnl_ct_set_src_port(struct nfnl_ct *, int, uint16_t);
-extern int		nfnl_ct_test_src_port(const struct nfnl_ct *, int);
-extern uint16_t		nfnl_ct_get_src_port(const struct nfnl_ct *, int);
+extern void	nfnl_ct_set_src_port(struct nfnl_ct *, int, uint16_t);
+extern int	nfnl_ct_test_src_port(const struct nfnl_ct *, int);
+extern uint16_t	nfnl_ct_get_src_port(const struct nfnl_ct *, int);
 
-extern void		nfnl_ct_set_dst_port(struct nfnl_ct *, int, uint16_t);
-extern int		nfnl_ct_test_dst_port(const struct nfnl_ct *, int);
-extern uint16_t		nfnl_ct_get_dst_port(const struct nfnl_ct *, int);
+extern void	nfnl_ct_set_dst_port(struct nfnl_ct *, int, uint16_t);
+extern int	nfnl_ct_test_dst_port(const struct nfnl_ct *, int);
+extern uint16_t	nfnl_ct_get_dst_port(const struct nfnl_ct *, int);
 
-extern void		nfnl_ct_set_icmp_id(struct nfnl_ct *, int, uint16_t);
-extern int		nfnl_ct_test_icmp_id(const struct nfnl_ct *, int);
-extern uint16_t		nfnl_ct_get_icmp_id(const struct nfnl_ct *, int);
+extern void	nfnl_ct_set_icmp_id(struct nfnl_ct *, int, uint16_t);
+extern int	nfnl_ct_test_icmp_id(const struct nfnl_ct *, int);
+extern uint16_t	nfnl_ct_get_icmp_id(const struct nfnl_ct *, int);
 
-extern void		nfnl_ct_set_icmp_type(struct nfnl_ct *, int, uint8_t);
-extern int		nfnl_ct_test_icmp_type(const struct nfnl_ct *, int);
-extern uint8_t		nfnl_ct_get_icmp_type(const struct nfnl_ct *, int);
+extern void	nfnl_ct_set_icmp_type(struct nfnl_ct *, int, uint8_t);
+extern int	nfnl_ct_test_icmp_type(const struct nfnl_ct *, int);
+extern uint8_t	nfnl_ct_get_icmp_type(const struct nfnl_ct *, int);
 
-extern void		nfnl_ct_set_icmp_code(struct nfnl_ct *, int, uint8_t);
-extern int		nfnl_ct_test_icmp_code(const struct nfnl_ct *, int);
-extern uint8_t		nfnl_ct_get_icmp_code(const struct nfnl_ct *, int);
+extern void	nfnl_ct_set_icmp_code(struct nfnl_ct *, int, uint8_t);
+extern int	nfnl_ct_test_icmp_code(const struct nfnl_ct *, int);
+extern uint8_t	nfnl_ct_get_icmp_code(const struct nfnl_ct *, int);
 
-extern void		nfnl_ct_set_packets(struct nfnl_ct *, int, uint64_t);
-extern int		nfnl_ct_test_packets(const struct nfnl_ct *, int);
-extern uint64_t		nfnl_ct_get_packets(const struct nfnl_ct *,int);
+extern void	nfnl_ct_set_packets(struct nfnl_ct *, int, uint64_t);
+extern int	nfnl_ct_test_packets(const struct nfnl_ct *, int);
+extern uint64_t	nfnl_ct_get_packets(const struct nfnl_ct *,int);
 
-extern void		nfnl_ct_set_bytes(struct nfnl_ct *, int, uint64_t);
-extern int		nfnl_ct_test_bytes(const struct nfnl_ct *, int);
-extern uint64_t		nfnl_ct_get_bytes(const struct nfnl_ct *, int);
+extern void	nfnl_ct_set_bytes(struct nfnl_ct *, int, uint64_t);
+extern int	nfnl_ct_test_bytes(const struct nfnl_ct *, int);
+extern uint64_t	nfnl_ct_get_bytes(const struct nfnl_ct *, int);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/netfilter/log.h b/include/netlink/netfilter/log.h
index e65cc58..2002fa8 100644
--- a/include/netlink/netfilter/log.h
+++ b/include/netlink/netfilter/log.h
@@ -9,6 +9,7 @@
  * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
  */
 
 #ifndef NETLINK_LOG_H_
@@ -20,82 +21,85 @@
 extern "C" {
 #endif
 
-struct nl_handle;
+struct nl_sock;
 struct nlmsghdr;
 struct nfnl_log;
 
 extern struct nl_object_ops log_obj_ops;
 
+enum nfnl_log_copy_mode {
+	NFNL_LOG_COPY_NONE,
+	NFNL_LOG_COPY_META,
+	NFNL_LOG_COPY_PACKET,
+};
+
+enum nfnl_log_flags {
+	NFNL_LOG_FLAG_SEQ		= 0x1,
+	NFNL_LOG_FLAG_SEQ_GLOBAL	= 0x2,
+};
+
 /* General */
-extern struct nfnl_log *nfnl_log_alloc(void);
-extern struct nfnl_log *nfnlmsg_log_parse(struct nlmsghdr *);
+extern struct nfnl_log *	nfnl_log_alloc(void);
+extern int			nfnlmsg_log_parse(struct nlmsghdr *,
+						  struct nfnl_log **);
 
-extern void		nfnl_log_get(struct nfnl_log *);
-extern void		nfnl_log_put(struct nfnl_log *);
+extern void			nfnl_log_get(struct nfnl_log *);
+extern void			nfnl_log_put(struct nfnl_log *);
 
-extern struct nl_msg *	nfnl_log_build_bind(uint16_t);;
-extern int		nfnl_log_bind(struct nl_handle *, uint16_t);
-extern struct nl_msg *	nfnl_log_build_unbind(uint16_t);
-extern int		nfnl_log_unbind(struct nl_handle *, uint16_t);
-extern struct nl_msg *	nfnl_log_build_pf_bind(uint8_t);
-extern int		nfnl_log_pf_bind(struct nl_handle *, uint8_t);
-extern struct nl_msg *	nfnl_log_build_pf_unbind(uint8_t);
-extern int		nfnl_log_pf_unbind(struct nl_handle *, uint8_t);
-extern struct nl_msg *	nfnl_log_build_mode(uint16_t, uint8_t, uint32_t);
-extern int		nfnl_log_set_mode(struct nl_handle *, uint16_t,
-					  uint8_t, uint32_t);
+/* Attributes */
+extern void			nfnl_log_set_group(struct nfnl_log *, uint16_t);
+extern int			nfnl_log_test_group(const struct nfnl_log *);
+extern uint16_t			nfnl_log_get_group(const struct nfnl_log *);
 
-extern void		nfnl_log_set_family(struct nfnl_log *, uint8_t);
-extern uint8_t		nfnl_log_get_family(const struct nfnl_log *);
+extern void			nfnl_log_set_copy_mode(struct nfnl_log *,
+						       enum nfnl_log_copy_mode);
+extern int			nfnl_log_test_copy_mode(const struct nfnl_log *);
+extern enum nfnl_log_copy_mode	nfnl_log_get_copy_mode(const struct nfnl_log *);
 
-extern void		nfnl_log_set_hwproto(struct nfnl_log *, uint16_t);
-extern int		nfnl_log_test_hwproto(const struct nfnl_log *);
-extern uint16_t		nfnl_log_get_hwproto(const struct nfnl_log *);
+extern char *			nfnl_log_copy_mode2str(enum nfnl_log_copy_mode,
+						       char *, size_t);
+extern enum nfnl_log_copy_mode	nfnl_log_str2copy_mode(const char *);
 
-extern void		nfnl_log_set_hook(struct nfnl_log *, uint8_t);
-extern int		nfnl_log_test_hook(const struct nfnl_log *);
-extern uint8_t		nfnl_log_get_hook(const struct nfnl_log *);
+extern void			nfnl_log_set_copy_range(struct nfnl_log *, uint32_t);
+extern int			nfnl_log_test_copy_range(const struct nfnl_log *);
+extern uint32_t			nfnl_log_get_copy_range(const struct nfnl_log *);
 
-extern void		nfnl_log_set_mark(struct nfnl_log *, uint32_t);
-extern int		nfnl_log_test_mark(const struct nfnl_log *);
-extern uint32_t		nfnl_log_get_mark(const struct nfnl_log *);
+extern void			nfnl_log_set_flush_timeout(struct nfnl_log *, uint32_t);
+extern int			nfnl_log_test_flush_timeout(const struct nfnl_log *);
+extern uint32_t			nfnl_log_get_flush_timeout(const struct nfnl_log *);
 
-extern void		nfnl_log_set_timestamp(struct nfnl_log *,
-					       struct timeval *);
-extern const struct timeval *nfnl_log_get_timestamp(const struct nfnl_log *);
+extern void			nfnl_log_set_alloc_size(struct nfnl_log *, uint32_t);
+extern int			nfnl_log_test_alloc_size(const struct nfnl_log *);
+extern uint32_t			nfnl_log_get_alloc_size(const struct nfnl_log *);
 
-extern void		nfnl_log_set_indev(struct nfnl_log *, uint32_t);
-extern uint32_t		nfnl_log_get_indev(const struct nfnl_log *);
+extern void			nfnl_log_set_queue_threshold(struct nfnl_log *, uint32_t);
+extern int			nfnl_log_test_queue_threshold(const struct nfnl_log *);
+extern uint32_t			nfnl_log_get_queue_threshold(const struct nfnl_log *);
 
-extern void		nfnl_log_set_outdev(struct nfnl_log *, uint32_t);
-extern uint32_t		nfnl_log_get_outdev(const struct nfnl_log *);
+extern void			nfnl_log_set_flags(struct nfnl_log *, unsigned int);
+extern void			nfnl_log_unset_flags(struct nfnl_log *, unsigned int);
+extern unsigned int		nfnl_log_get_flags(const struct nfnl_log *);
 
-extern void		nfnl_log_set_physindev(struct nfnl_log *, uint32_t);
-extern uint32_t		nfnl_log_get_physindev(const struct nfnl_log *);
+extern char *			nfnl_log_flags2str(unsigned int, char *, size_t);
+extern unsigned int		nfnl_log_str2flags(const char *);
 
-extern void		nfnl_log_set_physoutdev(struct nfnl_log *, uint32_t);
-extern uint32_t		nfnl_log_get_physoutdev(const struct nfnl_log *);
+extern int	nfnl_log_build_pf_bind(uint8_t, struct nl_msg **);
+extern int	nfnl_log_pf_bind(struct nl_sock *, uint8_t);
 
-extern void		nfnl_log_set_hwaddr(struct nfnl_log *, uint8_t *, int);
-extern const uint8_t *	nfnl_log_get_hwaddr(const struct nfnl_log *, int *);
+extern int	nfnl_log_build_pf_unbind(uint8_t, struct nl_msg **);
+extern int	nfnl_log_pf_unbind(struct nl_sock *, uint8_t);
 
-extern int		nfnl_log_set_payload(struct nfnl_log *, uint8_t *, int);
-extern const void *	nfnl_log_get_payload(const struct nfnl_log *, int *);
+extern int	nfnl_log_build_create_request(const struct nfnl_log *,
+					      struct nl_msg **);
+extern int	nfnl_log_create(struct nl_sock *, const struct nfnl_log *);
 
-extern int		nfnl_log_set_prefix(struct nfnl_log *, void *);
-extern const char *	nfnl_log_get_prefix(const struct nfnl_log *);
+extern int	nfnl_log_build_change_request(const struct nfnl_log *,
+					      struct nl_msg **);
+extern int	nfnl_log_change(struct nl_sock *, const struct nfnl_log *);
 
-extern void		nfnl_log_set_uid(struct nfnl_log *, uint32_t);
-extern int		nfnl_log_test_uid(const struct nfnl_log *);
-extern uint32_t		nfnl_log_get_uid(const struct nfnl_log *);
-
-extern void		nfnl_log_set_seq(struct nfnl_log *, uint32_t);
-extern int		nfnl_log_test_seq(const struct nfnl_log *);
-extern uint32_t		nfnl_log_get_seq(const struct nfnl_log *);
-
-extern void		nfnl_log_set_seq_global(struct nfnl_log *, uint32_t);
-extern int		nfnl_log_test_seq_global(const struct nfnl_log *);
-extern uint32_t		nfnl_log_get_seq_global(const struct nfnl_log *);
+extern int	nfnl_log_build_delete_request(const struct nfnl_log *,
+					      struct nl_msg **);
+extern int	nfnl_log_delete(struct nl_sock *, const struct nfnl_log *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/netfilter/log_msg.h b/include/netlink/netfilter/log_msg.h
new file mode 100644
index 0000000..63b0f64
--- /dev/null
+++ b/include/netlink/netfilter/log_msg.h
@@ -0,0 +1,98 @@
+/*
+ * netlink/netfilter/log_msg.h	Netfilter Log Message
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ */
+
+#ifndef NETLINK_LOG_MSG_H_
+#define NETLINK_LOG_MSG_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nlmsghdr;
+struct nfnl_log_msg;
+
+extern struct nl_object_ops log_msg_obj_ops;
+
+/* General */
+extern struct nfnl_log_msg *nfnl_log_msg_alloc(void);
+extern int		nfnlmsg_log_msg_parse(struct nlmsghdr *,
+					      struct nfnl_log_msg **);
+
+extern void		nfnl_log_msg_get(struct nfnl_log_msg *);
+extern void		nfnl_log_msg_put(struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_family(struct nfnl_log_msg *, uint8_t);
+extern uint8_t		nfnl_log_msg_get_family(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_hwproto(struct nfnl_log_msg *, uint16_t);
+extern int		nfnl_log_msg_test_hwproto(const struct nfnl_log_msg *);
+extern uint16_t		nfnl_log_msg_get_hwproto(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_hook(struct nfnl_log_msg *, uint8_t);
+extern int		nfnl_log_msg_test_hook(const struct nfnl_log_msg *);
+extern uint8_t		nfnl_log_msg_get_hook(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_mark(struct nfnl_log_msg *, uint32_t);
+extern int		nfnl_log_msg_test_mark(const struct nfnl_log_msg *);
+extern uint32_t		nfnl_log_msg_get_mark(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_timestamp(struct nfnl_log_msg *,
+					       struct timeval *);
+extern const struct timeval *nfnl_log_msg_get_timestamp(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_indev(struct nfnl_log_msg *, uint32_t);
+extern uint32_t		nfnl_log_msg_get_indev(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_outdev(struct nfnl_log_msg *, uint32_t);
+extern uint32_t		nfnl_log_msg_get_outdev(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_physindev(struct nfnl_log_msg *, uint32_t);
+extern uint32_t		nfnl_log_msg_get_physindev(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_physoutdev(struct nfnl_log_msg *, uint32_t);
+extern uint32_t		nfnl_log_msg_get_physoutdev(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_hwaddr(struct nfnl_log_msg *, uint8_t *, int);
+extern const uint8_t *	nfnl_log_msg_get_hwaddr(const struct nfnl_log_msg *, int *);
+
+extern int		nfnl_log_msg_set_payload(struct nfnl_log_msg *, uint8_t *, int);
+extern const void *	nfnl_log_msg_get_payload(const struct nfnl_log_msg *, int *);
+
+extern int		nfnl_log_msg_set_prefix(struct nfnl_log_msg *, void *);
+extern const char *	nfnl_log_msg_get_prefix(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_uid(struct nfnl_log_msg *, uint32_t);
+extern int		nfnl_log_msg_test_uid(const struct nfnl_log_msg *);
+extern uint32_t		nfnl_log_msg_get_uid(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_gid(struct nfnl_log_msg *, uint32_t);
+extern int		nfnl_log_msg_test_gid(const struct nfnl_log_msg *);
+extern uint32_t		nfnl_log_msg_get_gid(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_seq(struct nfnl_log_msg *, uint32_t);
+extern int		nfnl_log_msg_test_seq(const struct nfnl_log_msg *);
+extern uint32_t		nfnl_log_msg_get_seq(const struct nfnl_log_msg *);
+
+extern void		nfnl_log_msg_set_seq_global(struct nfnl_log_msg *, uint32_t);
+extern int		nfnl_log_msg_test_seq_global(const struct nfnl_log_msg *);
+extern uint32_t		nfnl_log_msg_get_seq_global(const struct nfnl_log_msg *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/include/netlink/netfilter/netfilter.h b/include/netlink/netfilter/netfilter.h
new file mode 100644
index 0000000..dd3589c
--- /dev/null
+++ b/include/netlink/netfilter/netfilter.h
@@ -0,0 +1,31 @@
+/*
+ * netlink/netfilter/netfilter.h	Netfilter generic functions
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ */
+
+#ifndef NETLINK_NETFILTER_H_
+#define NETLINK_NETFILTER_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char *			nfnl_verdict2str(unsigned int, char *, size_t);
+extern unsigned int		nfnl_str2verdict(const char *);
+
+extern char *			nfnl_inet_hook2str(unsigned int, char *, size_t);
+extern unsigned int		nfnl_str2inet_hook(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/netfilter/nfnl.h b/include/netlink/netfilter/nfnl.h
index 123d93e..8da4ba1 100644
--- a/include/netlink/netfilter/nfnl.h
+++ b/include/netlink/netfilter/nfnl.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
  */
@@ -23,14 +23,14 @@
 #define NFNL_HDRLEN NLMSG_ALIGN(sizeof(struct nfgenmsg))
 #define NFNLMSG_TYPE(subsys, subtype) (((subsys) << 8) | (subtype))
 
-extern int		nfnl_connect(struct nl_handle *);
+extern int		nfnl_connect(struct nl_sock *);
 
 extern uint8_t		nfnlmsg_subsys(struct nlmsghdr *);
 extern uint8_t		nfnlmsg_subtype(struct nlmsghdr *);
 extern uint8_t		nfnlmsg_family(struct nlmsghdr *);
 extern uint16_t		nfnlmsg_res_id(struct nlmsghdr *);
 
-extern int		nfnl_send_simple(struct nl_handle *, uint8_t, uint8_t,
+extern int		nfnl_send_simple(struct nl_sock *, uint8_t, uint8_t,
 					 int, uint8_t, uint16_t);
 extern struct nl_msg *	nfnlmsg_alloc_simple(uint8_t, uint8_t, int,
 					     uint8_t, uint16_t);
diff --git a/include/netlink/netfilter/queue.h b/include/netlink/netfilter/queue.h
new file mode 100644
index 0000000..664610d
--- /dev/null
+++ b/include/netlink/netfilter/queue.h
@@ -0,0 +1,90 @@
+/*
+ * netlink/netfilter/queue.h	Netfilter Queue
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
+ */
+
+#ifndef NETLINK_QUEUE_H_
+#define NETLINK_QUEUE_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nl_sock;
+struct nlmsghdr;
+struct nfnl_queue;
+
+extern struct nl_object_ops queue_obj_ops;
+
+enum nfnl_queue_copy_mode {
+	NFNL_QUEUE_COPY_NONE,
+	NFNL_QUEUE_COPY_META,
+	NFNL_QUEUE_COPY_PACKET,
+};
+
+/* General */
+extern struct nl_sock *		nfnl_queue_socket_alloc(void);
+
+extern struct nfnl_queue *	nfnl_queue_alloc(void);
+
+extern void			nfnl_queue_get(struct nfnl_queue *);
+extern void			nfnl_queue_put(struct nfnl_queue *);
+
+/* Attributes */
+extern void			nfnl_queue_set_group(struct nfnl_queue *, uint16_t);
+extern int			nfnl_queue_test_group(const struct nfnl_queue *);
+extern uint16_t			nfnl_queue_get_group(const struct nfnl_queue *);
+
+extern void			nfnl_queue_set_maxlen(struct nfnl_queue *, uint32_t);
+extern int			nfnl_queue_test_maxlen(const struct nfnl_queue *);
+extern uint32_t			nfnl_queue_get_maxlen(const struct nfnl_queue *);
+
+extern void			nfnl_queue_set_copy_mode(struct nfnl_queue *,
+							 enum nfnl_queue_copy_mode);
+extern int			nfnl_queue_test_copy_mode(const struct nfnl_queue *);
+extern enum nfnl_queue_copy_mode nfnl_queue_get_copy_mode(const struct nfnl_queue *);
+
+extern char *			nfnl_queue_copy_mode2str(enum nfnl_queue_copy_mode,
+							 char *, size_t);
+extern enum nfnl_queue_copy_mode nfnl_queue_str2copy_mode(const char *);
+
+extern void			nfnl_queue_set_copy_range(struct nfnl_queue *,
+							  uint32_t);
+extern int			nfnl_queue_test_copy_range(const struct nfnl_queue *);
+extern uint32_t			nfnl_queue_get_copy_range(const struct nfnl_queue *);
+
+extern int	nfnl_queue_build_pf_bind(uint8_t, struct nl_msg **);
+extern int	nfnl_queue_pf_bind(struct nl_sock *, uint8_t);
+
+extern int	nfnl_queue_build_pf_unbind(uint8_t, struct nl_msg **);
+extern int	nfnl_queue_pf_unbind(struct nl_sock *, uint8_t);
+
+extern int	nfnl_queue_build_create_request(const struct nfnl_queue *,
+						struct nl_msg **);
+extern int	nfnl_queue_create(struct nl_sock *,
+				  const struct nfnl_queue *);
+
+extern int	nfnl_queue_build_change_request(const struct nfnl_queue *,
+						struct nl_msg **);
+extern int	nfnl_queue_change(struct nl_sock *,
+				  const struct nfnl_queue *);
+
+extern int	nfnl_queue_build_delete_request(const struct nfnl_queue *,
+						struct nl_msg **);
+extern int	nfnl_queue_delete(struct nl_sock *,
+				  const struct nfnl_queue *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/include/netlink/netfilter/queue_msg.h b/include/netlink/netfilter/queue_msg.h
new file mode 100644
index 0000000..24ed081
--- /dev/null
+++ b/include/netlink/netfilter/queue_msg.h
@@ -0,0 +1,104 @@
+/*
+ * netlink/netfilter/queue_msg.h	Netfilter Queue Messages
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
+ */
+
+#ifndef NETLINK_QUEUE_MSG_H_
+#define NETLINK_QUEUE_MSG_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nl_sock;
+struct nlmsghdr;
+struct nfnl_queue_msg;
+
+extern struct nl_object_ops queue_msg_obj_ops;
+
+/* General */
+extern struct nfnl_queue_msg *	nfnl_queue_msg_alloc(void);
+extern int			nfnlmsg_queue_msg_parse(struct nlmsghdr *,
+						struct nfnl_queue_msg **);
+
+extern void			nfnl_queue_msg_get(struct nfnl_queue_msg *);
+extern void			nfnl_queue_msg_put(struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_group(struct nfnl_queue_msg *, uint16_t);
+extern int			nfnl_queue_msg_test_group(const struct nfnl_queue_msg *);
+extern uint16_t			nfnl_queue_msg_get_group(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_family(struct nfnl_queue_msg *, uint8_t);
+extern int			nfnl_queue_msg_test_family(const struct nfnl_queue_msg *);
+extern uint8_t			nfnl_queue_msg_get_family(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_packetid(struct nfnl_queue_msg *, uint32_t);
+extern int			nfnl_queue_msg_test_packetid(const struct nfnl_queue_msg *);
+extern uint32_t			nfnl_queue_msg_get_packetid(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_hwproto(struct nfnl_queue_msg *, uint16_t);
+extern int			nfnl_queue_msg_test_hwproto(const struct nfnl_queue_msg *);
+extern uint16_t			nfnl_queue_msg_get_hwproto(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_hook(struct nfnl_queue_msg *, uint8_t);
+extern int			nfnl_queue_msg_test_hook(const struct nfnl_queue_msg *);
+extern uint8_t			nfnl_queue_msg_get_hook(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_mark(struct nfnl_queue_msg *, uint32_t);
+extern int			nfnl_queue_msg_test_mark(const struct nfnl_queue_msg *);
+extern uint32_t			nfnl_queue_msg_get_mark(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_timestamp(struct nfnl_queue_msg *,
+							      struct timeval *);
+extern int			nfnl_queue_msg_test_timestamp(const struct nfnl_queue_msg *);
+extern const struct timeval *	nfnl_queue_msg_get_timestamp(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_indev(struct nfnl_queue_msg *, uint32_t);
+extern int			nfnl_queue_msg_test_indev(const struct nfnl_queue_msg *);
+extern uint32_t			nfnl_queue_msg_get_indev(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_outdev(struct nfnl_queue_msg *, uint32_t);
+extern int			nfnl_queue_msg_test_outdev(const struct nfnl_queue_msg *);
+extern uint32_t			nfnl_queue_msg_get_outdev(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_physindev(struct nfnl_queue_msg *, uint32_t);
+extern int			nfnl_queue_msg_test_physindev(const struct nfnl_queue_msg *);
+extern uint32_t			nfnl_queue_msg_get_physindev(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_physoutdev(struct nfnl_queue_msg *, uint32_t);
+extern int			nfnl_queue_msg_test_physoutdev(const struct nfnl_queue_msg *);
+extern uint32_t			nfnl_queue_msg_get_physoutdev(const struct nfnl_queue_msg *);
+
+extern void			nfnl_queue_msg_set_hwaddr(struct nfnl_queue_msg *, uint8_t *, int);
+extern int			nfnl_queue_msg_test_hwaddr(const struct nfnl_queue_msg *);
+extern const uint8_t *		nfnl_queue_msg_get_hwaddr(const struct nfnl_queue_msg *, int *);
+
+extern int			nfnl_queue_msg_set_payload(struct nfnl_queue_msg *, uint8_t *, int);
+extern int			nfnl_queue_msg_test_payload(const struct nfnl_queue_msg *);
+extern const void *		nfnl_queue_msg_get_payload(const struct nfnl_queue_msg *, int *);
+
+extern void			nfnl_queue_msg_set_verdict(struct nfnl_queue_msg *,
+							   unsigned int);
+extern int			nfnl_queue_msg_test_verdict(const struct nfnl_queue_msg *);
+extern unsigned int		nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *);
+
+extern struct nl_msg *		nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *);
+extern int			nfnl_queue_msg_send_verdict(struct nl_sock *,
+							    const struct nfnl_queue_msg *);
+extern int			nfnl_queue_msg_send_verdict_payload(struct nl_sock *,
+						const struct nfnl_queue_msg *,
+						const void *, unsigned );
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/include/netlink/netlink.h b/include/netlink/netlink.h
index 2cdf345..1cfe220 100644
--- a/include/netlink/netlink.h
+++ b/include/netlink/netlink.h
@@ -20,11 +20,14 @@
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/time.h>
+#include <netdb.h>
 #include <netlink/netlink-compat.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/genetlink.h>
 #include <linux/netfilter/nfnetlink.h>
+#include <netlink/version.h>
+#include <netlink/errno.h>
 #include <netlink/types.h>
 #include <netlink/handlers.h>
 #include <netlink/socket.h>
@@ -33,34 +36,39 @@
 extern "C" {
 #endif
 
+struct ucred;
+
 extern int nl_debug;
 extern struct nl_dump_params nl_debug_dp;
 
 /* Connection Management */
-extern int			nl_connect(struct nl_handle *, int);
-extern void			nl_close(struct nl_handle *);
+extern int			nl_connect(struct nl_sock *, int);
+extern void			nl_close(struct nl_sock *);
 
 /* Send */
-extern int			nl_sendto(struct nl_handle *, void *, size_t);
-extern int			nl_sendmsg(struct nl_handle *, struct nl_msg *,
+extern int			nl_sendto(struct nl_sock *, void *, size_t);
+extern int			nl_sendmsg(struct nl_sock *, struct nl_msg *,
 					   struct msghdr *);
-extern int			nl_send(struct nl_handle *, struct nl_msg *);
-extern int			nl_send_auto_complete(struct nl_handle *,
+extern int			nl_send(struct nl_sock *, struct nl_msg *);
+extern int			nl_send_iovec(struct nl_sock *, struct nl_msg *,
+					      struct iovec *, unsigned);
+extern void			nl_auto_complete(struct nl_sock *,
 						      struct nl_msg *);
-extern int			nl_send_simple(struct nl_handle *, int, int,
+extern int			nl_send_auto_complete(struct nl_sock *,
+						      struct nl_msg *);
+extern int			nl_send_simple(struct nl_sock *, int, int,
 					       void *, size_t);
 
 /* Receive */
-extern int			nl_recv(struct nl_handle *,
+extern int			nl_recv(struct nl_sock *,
 					struct sockaddr_nl *, unsigned char **,
 					struct ucred **);
 
-extern int			nl_recvmsgs(struct nl_handle *, struct nl_cb *);
+extern int			nl_recvmsgs(struct nl_sock *, struct nl_cb *);
 
-#define nl_recvmsgs_def(handle) nl_recvmsgs_default(handle)
-extern int			nl_recvmsgs_default(struct nl_handle *);
+extern int			nl_recvmsgs_default(struct nl_sock *);
 
-extern int			nl_wait_for_ack(struct nl_handle *);
+extern int			nl_wait_for_ack(struct nl_sock *);
 
 /* Netlink Family Translations */
 extern char *			nl_nlfamily2str(int, char *, size_t);
diff --git a/include/netlink/object-api.h b/include/netlink/object-api.h
index 2a32f2c..b3337f0 100644
--- a/include/netlink/object-api.h
+++ b/include/netlink/object-api.h
@@ -94,7 +94,6 @@
  * 				   struct nl_dump_params *params)
  * {
  * 	struct my_obj *my_obj = nl_object_priv(obj);
- * 	int line = 1;	// We will print at least one line for sure
  *
  * 	// It is absolutely essential to use nl_dump() when printing
  *	// any text to make sure the dumping parameters are respected.
@@ -102,14 +101,11 @@
  *
  * 	// Before we can dump the next line, make sure to prefix
  *	// this line correctly.
- * 	nl_new_line(params, line++);
+ * 	nl_new_line(params);
  *
  * 	// You may also split a line into multiple nl_dump() calls.
  * 	nl_dump(params, "String: %s ", my_obj->my_string);
  * 	nl_dump(params, "String-2: %s\n", my_obj->another_string);
- *
- * 	// Return the number of lines dumped
- * 	return line;
  * }
  *
  * struct nl_object_ops my_ops = {
@@ -202,7 +198,18 @@
  *
  * @return True if the attribute is available, otherwise false is returned.
  */
-#define AVAILABLE(A, B, ATTR)	(((A)->ce_mask & (B)->ce_mask) & (ATTR))
+#define AVAILABLE(A, B, ATTR)		(((A)->ce_mask & (B)->ce_mask) & (ATTR))
+
+/**
+ * Return true if attribute is available in only one of both objects
+ * @arg A		an object
+ * @arg B		another object
+ * @arg ATTR		attribute bit
+ *
+ * @return True if the attribute is available in only one of both objects,
+ * otherwise false is returned.
+ */
+#define AVAILABLE_MISMATCH(A, B, ATTR)	(((A)->ce_mask ^ (B)->ce_mask) & (ATTR))
 
 /**
  * Return true if attributes mismatch
@@ -219,7 +226,8 @@
  *
  * @return True if the attribute mismatch, or false if they match.
  */
-#define ATTR_MISMATCH(A, B, ATTR, EXPR)	(!AVAILABLE(A, B, ATTR) || (EXPR))
+#define ATTR_MISMATCH(A, B, ATTR, EXPR)	(AVAILABLE_MISMATCH(A, B, ATTR) || \
+					 (AVAILABLE(A, B, ATTR) && (EXPR)))
 
 /**
  * Return attribute bit if attribute does not match
@@ -304,8 +312,8 @@
 	 *
 	 * The functions must return the number of lines printed.
 	 */
-	int   (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *,
-					struct nl_dump_params *);
+	void (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *,
+				       struct nl_dump_params *);
 
 	/**
 	 * Comparison function
diff --git a/include/netlink/object.h b/include/netlink/object.h
index 751a1b3..ef1ed9f 100644
--- a/include/netlink/object.h
+++ b/include/netlink/object.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_OBJECT_H_
@@ -27,7 +27,8 @@
 
 /* General */
 extern struct nl_object *	nl_object_alloc(struct nl_object_ops *);
-extern struct nl_object *	nl_object_alloc_name(const char *);
+extern int			nl_object_alloc_name(const char *,
+						     struct nl_object **);
 extern void			nl_object_free(struct nl_object *);
 extern struct nl_object *	nl_object_clone(struct nl_object *obj);
 extern void			nl_object_get(struct nl_object *);
@@ -55,7 +56,11 @@
 /* Access Functions */
 extern int			nl_object_get_refcnt(struct nl_object *);
 extern struct nl_cache *	nl_object_get_cache(struct nl_object *);
-extern inline void *		nl_object_priv(struct nl_object *);
+static inline void *		nl_object_priv(struct nl_object *obj)
+{
+	return obj;
+}
+
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/addr.h b/include/netlink/route/addr.h
index 71a90e0..1381486 100644
--- a/include/netlink/route/addr.h
+++ b/include/netlink/route/addr.h
@@ -6,8 +6,8 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- *                         Baruch Even <baruch@ev-en.org>,
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2006 Baruch Even <baruch@ev-en.org>,
  *                         Mediatrix Telecom, inc. <ericb@mediatrix.com>
  */
 
@@ -26,63 +26,63 @@
 
 /* General */
 extern struct rtnl_addr *rtnl_addr_alloc(void);
-extern void		rtnl_addr_put(struct rtnl_addr *);
+extern void	rtnl_addr_put(struct rtnl_addr *);
 
-extern struct nl_cache *rtnl_addr_alloc_cache(struct nl_handle *);
+extern int	rtnl_addr_alloc_cache(struct nl_sock *, struct nl_cache **);
 
-/* Address Addition */
-extern struct nl_msg *	rtnl_addr_build_add_request(struct rtnl_addr *, int);
-extern int		rtnl_addr_add(struct nl_handle *, struct rtnl_addr *,
-				      int);
+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);
 
-/* Address Deletion */
-extern struct nl_msg *	rtnl_addr_build_delete_request(struct rtnl_addr *, int);
-extern int		rtnl_addr_delete(struct nl_handle *,
-					 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);
 
-/* Address Flags Translations */
-extern char *		rtnl_addr_flags2str(int, char *, size_t);
-extern int		rtnl_addr_str2flags(const char *);
+extern char *	rtnl_addr_flags2str(int, char *, size_t);
+extern int	rtnl_addr_str2flags(const char *);
 
-/* Attribute Access */
-extern void		rtnl_addr_set_label(struct rtnl_addr *, const char *);
-extern char *		rtnl_addr_get_label(struct rtnl_addr *);
+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_ifindex(struct rtnl_addr *, int);
+extern int	rtnl_addr_get_ifindex(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_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_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_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 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 *,
+extern int	rtnl_addr_set_local(struct rtnl_addr *,
 					    struct nl_addr *);
-extern struct nl_addr *	rtnl_addr_get_local(struct rtnl_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_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_broadcast(struct rtnl_addr *, struct nl_addr *);
+extern struct nl_addr *rtnl_addr_get_broadcast(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 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_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 *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/class-modules.h b/include/netlink/route/class-modules.h
index 2400a60..74a25c9 100644
--- a/include/netlink/route/class-modules.h
+++ b/include/netlink/route/class-modules.h
@@ -32,8 +32,8 @@
 	/**
 	 * Dump callbacks
 	 */
-	int (*co_dump[NL_DUMP_MAX+1])(struct rtnl_class *,
-				      struct nl_dump_params *, int);
+	void (*co_dump[NL_DUMP_MAX+1])(struct rtnl_class *,
+				       struct nl_dump_params *);
 
 	/**
 	 * Must return the contents supposed to be in TCA_OPTIONS
diff --git a/include/netlink/route/class.h b/include/netlink/route/class.h
index a624ef6..480095e 100644
--- a/include/netlink/route/class.h
+++ b/include/netlink/route/class.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_CLASS_H_
@@ -24,20 +24,25 @@
 
 extern struct nl_object_ops class_obj_ops;
 
-/* General */
 extern struct rtnl_class *	rtnl_class_alloc(void);
-extern void			rtnl_class_put(struct rtnl_class *);
-extern struct nl_cache *	rtnl_class_alloc_cache(struct nl_handle *, int);
+extern void		rtnl_class_put(struct rtnl_class *);
+extern int		rtnl_class_alloc_cache(struct nl_sock *, int,
+					       struct nl_cache **);
+extern struct rtnl_class *rtnl_class_get(struct nl_cache *, int, uint32_t);
 
 /* leaf qdisc access */
 extern struct rtnl_qdisc *	rtnl_class_leaf_qdisc(struct rtnl_class *,
 						      struct nl_cache *);
 
-/* class addition */
-extern struct nl_msg * rtnl_class_build_add_request(struct rtnl_class *, int);
-extern int rtnl_class_add(struct nl_handle *, struct rtnl_class *, int);
+extern int		rtnl_class_build_add_request(struct rtnl_class *, int,
+						     struct nl_msg **);
+extern int		rtnl_class_add(struct nl_sock *, struct rtnl_class *,
+				       int);
 
-/* attribute modification */
+extern int	rtnl_class_build_delete_request(struct rtnl_class *,
+											struct nl_msg **);
+extern int	rtnl_class_delete(struct nl_sock *, struct rtnl_class *);
+
 extern void		rtnl_class_set_ifindex(struct rtnl_class *, int);
 extern int		rtnl_class_get_ifindex(struct rtnl_class *);
 extern void		rtnl_class_set_handle(struct rtnl_class *, uint32_t);
diff --git a/include/netlink/route/classifier-modules.h b/include/netlink/route/classifier-modules.h
index 8c31e67..35cb06e 100644
--- a/include/netlink/route/classifier-modules.h
+++ b/include/netlink/route/classifier-modules.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_CLASS_MODULES_H_
@@ -25,19 +25,25 @@
 struct rtnl_cls_ops
 {
 	/**
-	 * Kind/Name of classifier
+	 * Name of classifier module
 	 */
 	char co_kind[32];
 
+
+	/**
+	 * Size of private classifier data
+	 */
+	size_t co_size;
+
 	/**
 	 * Dump callbacks
 	 */
-	int (*co_dump[NL_DUMP_MAX+1])(struct rtnl_cls *,
-				      struct nl_dump_params *, int);
+	void (*co_dump[NL_DUMP_MAX+1])(struct rtnl_cls *,
+				       struct nl_dump_params *);
 	/**
 	 * Must return the contents supposed to be in TCA_OPTIONS
 	 */
-	struct nl_msg *(*co_get_opts)(struct rtnl_cls *);
+	int (*co_get_opts)(struct rtnl_cls *, struct nl_msg *);
 
 	/**
 	 * TCA_OPTIONS message parser
diff --git a/include/netlink/route/classifier.h b/include/netlink/route/classifier.h
index 7ef0da4..d9c3d21 100644
--- a/include/netlink/route/classifier.h
+++ b/include/netlink/route/classifier.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_CLASSIFIER_H_
@@ -23,31 +23,37 @@
 
 extern struct nl_object_ops cls_obj_ops;
 
-extern struct		rtnl_cls *rtnl_cls_alloc(void);
-extern void		rtnl_cls_put(struct rtnl_cls *);
+extern struct rtnl_cls *rtnl_cls_alloc(void);
+extern void	rtnl_cls_put(struct rtnl_cls *);
 
-extern struct nl_cache *rtnl_cls_alloc_cache(struct nl_handle *, int, uint32_t);
+extern int	rtnl_cls_alloc_cache(struct nl_sock *, int, uint32_t,
+				     struct nl_cache **);
 
-/* classifier addition */
-extern int		rtnl_cls_add(struct nl_handle *, struct rtnl_cls *,
-				     int);
-extern struct nl_msg *	rtnl_cls_build_add_request(struct rtnl_cls *, int);
+extern int	rtnl_cls_build_add_request(struct rtnl_cls *, int,
+					   struct nl_msg **);
+extern int	rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int);
 
-extern struct nl_msg *rtnl_cls_build_change_request(struct rtnl_cls *, int);
-extern struct nl_msg *rtnl_cls_build_delete_request(struct rtnl_cls *, int);
-extern int  rtnl_cls_delete(struct nl_handle *, struct rtnl_cls *, int);
+extern int	rtnl_cls_build_change_request(struct rtnl_cls *, int,
+					      struct nl_msg **);
+extern int	rtnl_cls_build_delete_request(struct rtnl_cls *, int,
+					      struct nl_msg **);
+extern int	rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *, int);
 
-/* attribute modification */
 extern void rtnl_cls_set_ifindex(struct rtnl_cls *, int);
+extern int rtnl_cls_get_ifindex(struct rtnl_cls *);
 extern void rtnl_cls_set_handle(struct rtnl_cls *, uint32_t);
 extern void rtnl_cls_set_parent(struct rtnl_cls *, uint32_t);
-extern void rtnl_cls_set_kind(struct rtnl_cls *, const char *);
+extern uint32_t rtnl_cls_get_parent(struct rtnl_cls *);
+extern int rtnl_cls_set_kind(struct rtnl_cls *, const char *);
+extern struct rtnl_cls_ops *rtnl_cls_get_ops(struct rtnl_cls *);
 
-extern void rtnl_cls_set_prio(struct rtnl_cls *, int);
-extern int  rtnl_cls_get_prio(struct rtnl_cls *);
+extern void rtnl_cls_set_prio(struct rtnl_cls *, uint16_t);
+extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *);
 
-extern void rtnl_cls_set_protocol(struct rtnl_cls *, int);
-extern int  rtnl_cls_get_protocol(struct rtnl_cls *);
+extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t);
+extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *);
+
+extern void *rtnl_cls_data(struct rtnl_cls *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/cls/basic.h b/include/netlink/route/cls/basic.h
new file mode 100644
index 0000000..7003124
--- /dev/null
+++ b/include/netlink/route/cls/basic.h
@@ -0,0 +1,33 @@
+/*
+ * netlink/route/cls/basic.h	Basic Classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_BASIC_H_
+#define NETLINK_BASIC_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_cls_ops *rtnl_basic_get_ops(void);
+extern int	rtnl_basic_set_classid(struct rtnl_cls *, uint32_t);
+extern uint32_t	rtnl_basic_get_classid(struct rtnl_cls *);
+extern int	rtnl_basic_set_ematch(struct rtnl_cls *,
+				      struct rtnl_ematch_tree *);
+extern struct rtnl_ematch_tree *
+		rtnl_basic_get_ematch(struct rtnl_cls *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/cgroup.h b/include/netlink/route/cls/cgroup.h
new file mode 100644
index 0000000..7b0e3d3
--- /dev/null
+++ b/include/netlink/route/cls/cgroup.h
@@ -0,0 +1,31 @@
+/*
+ * netlink/route/cls/cgroup.h	Control Groups Classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CLS_CGROUP_H_
+#define NETLINK_CLS_CGROUP_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	rtnl_cgroup_set_ematch(struct rtnl_cls *,
+				       struct rtnl_ematch_tree *);
+extern struct rtnl_ematch_tree *
+		rtnl_cgroup_get_ematch(struct rtnl_cls *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/ematch.h b/include/netlink/route/cls/ematch.h
new file mode 100644
index 0000000..c4292bf
--- /dev/null
+++ b/include/netlink/route/cls/ematch.h
@@ -0,0 +1,73 @@
+/*
+ * netlink/route/cls/ematch.h		Extended Matches
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CLS_EMATCH_H_
+#define NETLINK_CLS_EMATCH_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/classifier.h>
+#include <linux/pkt_cls.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rtnl_ematch;
+struct rtnl_ematch_tree;
+
+struct rtnl_ematch_ops
+{
+	int				eo_kind;
+	const char *			eo_name;
+	size_t				eo_datalen;
+
+	int			      (*eo_parse)(struct rtnl_ematch *,
+						  void *, size_t);
+	void			      (*eo_dump)(struct rtnl_ematch *,
+						 struct nl_dump_params *);
+	struct nl_list_head		eo_list;
+};
+
+extern int	rtnl_ematch_register(struct rtnl_ematch_ops *);
+extern int	rtnl_ematch_unregister(struct rtnl_ematch_ops *);
+
+extern struct rtnl_ematch_ops *
+		rtnl_ematch_lookup_ops(int);
+extern struct rtnl_ematch_ops *
+		rtnl_ematch_lookup_ops_name(const char *);
+
+extern struct rtnl_ematch *
+		rtnl_ematch_alloc(struct rtnl_ematch_ops *);
+extern void	rtnl_ematch_add_child(struct rtnl_ematch *,
+				      struct rtnl_ematch *);
+extern void	rtnl_ematch_unlink(struct rtnl_ematch *);
+extern void	rtnl_ematch_free(struct rtnl_ematch *);
+
+extern void *	rtnl_ematch_data(struct rtnl_ematch *);
+extern void	rtnl_ematch_set_flags(struct rtnl_ematch *, uint16_t);
+extern void	rtnl_ematch_unset_flags(struct rtnl_ematch *, uint16_t);
+extern uint16_t	rtnl_ematch_get_flags(struct rtnl_ematch *);
+
+extern struct rtnl_ematch_tree *
+		rtnl_ematch_tree_alloc(uint16_t);
+extern void	rtnl_ematch_tree_free(struct rtnl_ematch_tree *);
+
+extern int	rtnl_ematch_parse(struct nlattr *, struct rtnl_ematch_tree **);
+extern void	rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *,
+					  struct rtnl_ematch *);
+extern void	rtnl_ematch_tree_dump(struct rtnl_ematch_tree *,
+				      struct nl_dump_params *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/ematch/cmp.h b/include/netlink/route/cls/ematch/cmp.h
new file mode 100644
index 0000000..b4ad03a
--- /dev/null
+++ b/include/netlink/route/cls/ematch/cmp.h
@@ -0,0 +1,31 @@
+/*
+ * netlink/route/cls/ematch/cmp.h	Simple Comparison
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CLS_EMATCH_CMP_H_
+#define NETLINK_CLS_EMATCH_CMP_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void	rtnl_ematch_cmp_set(struct rtnl_ematch *,
+				    struct tcf_em_cmp *);
+extern struct tcf_em_cmp *
+		rtnl_ematch_cmp_get(struct rtnl_ematch *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h
index caaa792..4b630f7 100644
--- a/include/netlink/route/link.h
+++ b/include/netlink/route/link.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_LINK_H_
@@ -51,116 +51,92 @@
 
 #define RTNL_LINK_STATS_MAX (__RTNL_LINK_STATS_MAX - 1)
 
-/**
- * Special interface index stating the link was not found.
- * @ingroup link
- */
-#define RTNL_LINK_NOT_FOUND -1
-
 /* link object allocation/freeage */
-extern struct rtnl_link *	rtnl_link_alloc(void);
-extern void			rtnl_link_put(struct rtnl_link *);
-extern void			rtnl_link_free(struct rtnl_link *);
+extern struct rtnl_link *rtnl_link_alloc(void);
+extern void	rtnl_link_put(struct rtnl_link *);
+extern void	rtnl_link_free(struct rtnl_link *);
 
 /* link cache management */
-extern struct nl_cache *	rtnl_link_alloc_cache(struct nl_handle *);
-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_alloc_cache(struct nl_sock *, struct nl_cache **);
+extern struct rtnl_link *rtnl_link_get(struct nl_cache *, int);
+extern struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *, const char *);
 
 
-/* Link Modifications */
-extern struct nl_msg *		rtnl_link_build_change_request(struct rtnl_link *,
-							       struct rtnl_link *,
-							       int);
-extern int			rtnl_link_change(struct nl_handle *,
-						 struct rtnl_link *,
-						 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);
 
 /* Name <-> Index Translations */
-extern char * 			rtnl_link_i2name(struct nl_cache *, int,
-						 char *, size_t);
-extern int			rtnl_link_name2i(struct nl_cache *,
-						 const char *);
+extern char * 	rtnl_link_i2name(struct nl_cache *, int, char *, size_t);
+extern int	rtnl_link_name2i(struct nl_cache *, const char *);
 
 /* Name <-> Statistic Translations */
-extern char *			rtnl_link_stat2str(int, char *, size_t);
-extern int			rtnl_link_str2stat(const char *);
+extern char *	rtnl_link_stat2str(int, char *, size_t);
+extern int	rtnl_link_str2stat(const char *);
 
 /* Link Flags Translations */
-extern char *			rtnl_link_flags2str(int, char *, size_t);
-extern int			rtnl_link_str2flags(const char *);
+extern char *	rtnl_link_flags2str(int, char *, size_t);
+extern int	rtnl_link_str2flags(const char *);
 
-extern char *			rtnl_link_operstate2str(int, char *, size_t);
-extern int			rtnl_link_str2operstate(const char *);
+extern char *	rtnl_link_operstate2str(int, char *, size_t);
+extern int	rtnl_link_str2operstate(const char *);
 
-extern char *			rtnl_link_mode2str(int, char *, size_t);
-extern int			rtnl_link_str2mode(const char *);
+extern char *	rtnl_link_mode2str(int, char *, size_t);
+extern int	rtnl_link_str2mode(const char *);
 
 /* Access Functions */
-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_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_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_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_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_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_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_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_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_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_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_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_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_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_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 void	rtnl_link_set_linkmode(struct rtnl_link *, uint8_t);
+extern uint8_t	rtnl_link_get_linkmode(struct rtnl_link *);
 
-extern uint64_t 		rtnl_link_get_stat(struct rtnl_link *, int);
+extern uint64_t rtnl_link_get_stat(struct rtnl_link *, int);
 
-extern int			rtnl_link_set_info_type(struct rtnl_link *,
-							const char *);
-extern char *			rtnl_link_get_info_type(struct rtnl_link *);
+extern int	rtnl_link_set_info_type(struct rtnl_link *, const char *);
+extern char *	rtnl_link_get_info_type(struct rtnl_link *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/link/info-api.h b/include/netlink/route/link/info-api.h
index 2ccce9d..7e18e31 100644
--- a/include/netlink/route/link/info-api.h
+++ b/include/netlink/route/link/info-api.h
@@ -45,8 +45,8 @@
 
 	/** Called when the link object is dumped.
 	 * Must dump the info type specific attributes. */
-	int	      (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
-						struct nl_dump_params *, int);
+	void	      (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
+						struct nl_dump_params *);
 
 	/** Called when a link object is cloned.
 	 * Must clone all info type specific attributes. */
diff --git a/include/netlink/route/neighbour.h b/include/netlink/route/neighbour.h
index 078c3f4..698539a 100644
--- a/include/netlink/route/neighbour.h
+++ b/include/netlink/route/neighbour.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_NEIGHBOUR_H_
@@ -22,39 +22,27 @@
 
 struct rtnl_neigh;
 
-/* neighbour object allocation/freeage */
-extern struct rtnl_neigh *	rtnl_neigh_alloc(void);
-extern void			rtnl_neigh_put(struct rtnl_neigh *);
+extern struct rtnl_neigh *rtnl_neigh_alloc(void);
+extern void	rtnl_neigh_put(struct rtnl_neigh *);
 
-/* neighbour cache management */
-extern struct nl_cache *	rtnl_neigh_alloc_cache(struct nl_handle *);
-extern struct rtnl_neigh *	rtnl_neigh_get(struct nl_cache *, int,
+extern int	rtnl_neigh_alloc_cache(struct nl_sock *, struct nl_cache **);
+extern struct rtnl_neigh *rtnl_neigh_get(struct nl_cache *, int,
 					       struct nl_addr *);
 
-/* Neigbour state translations */
-extern char *			rtnl_neigh_state2str(int, char *, size_t);
-extern int			rtnl_neigh_str2state(const char *);
+extern char *	rtnl_neigh_state2str(int, char *, size_t);
+extern int	rtnl_neigh_str2state(const char *);
 
-/* Neighbour flags translations */
-extern char *			rtnl_neigh_flags2str(int, char *, size_t);
-extern int			rtnl_neigh_str2flag(const char *);
+extern char *	rtnl_neigh_flags2str(int, char *, size_t);
+extern int	rtnl_neigh_str2flag(const char *);
 
-/* Neighbour Addition */
-extern int			rtnl_neigh_add(struct nl_handle *,
-					       struct rtnl_neigh *, int);
-extern struct nl_msg *		rtnl_neigh_build_add_request(struct rtnl_neigh *, int);
+extern int	rtnl_neigh_add(struct nl_sock *, struct rtnl_neigh *, int);
+extern int	rtnl_neigh_build_add_request(struct rtnl_neigh *, int,
+					     struct nl_msg **);
 
-/* Neighbour Modification */
-extern int			rtnl_neigh_change(struct nl_handle *,
-						  struct rtnl_neigh *, int);
-extern struct nl_msg *		rtnl_neigh_build_change_request(struct rtnl_neigh *, int);
+extern int	rtnl_neigh_delete(struct nl_sock *, struct rtnl_neigh *, int);
+extern int	rtnl_neigh_build_delete_request(struct rtnl_neigh *, int,
+						struct nl_msg **);
 
-/* Neighbour Deletion */
-extern int			rtnl_neigh_delete(struct nl_handle *,
-						  struct rtnl_neigh *, int);
-extern struct nl_msg *		rtnl_neigh_build_delete_request(struct rtnl_neigh *, int);
-
-/* Access functions */
 extern void			rtnl_neigh_set_state(struct rtnl_neigh *, int);
 extern int			rtnl_neigh_get_state(struct rtnl_neigh *);
 extern void			rtnl_neigh_unset_state(struct rtnl_neigh *,
diff --git a/include/netlink/route/neightbl.h b/include/netlink/route/neightbl.h
index 20285ee..412c3e9 100644
--- a/include/netlink/route/neightbl.h
+++ b/include/netlink/route/neightbl.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_NEIGHTBL_H_
@@ -25,15 +25,16 @@
 extern struct rtnl_neightbl *rtnl_neightbl_alloc(void);
 extern void rtnl_neightbl_put(struct rtnl_neightbl *);
 extern void rtnl_neightbl_free(struct rtnl_neightbl *);
-extern struct nl_cache *rtnl_neightbl_alloc_cache(struct nl_handle *);
+extern int rtnl_neightbl_alloc_cache(struct nl_sock *, struct nl_cache **);
 extern struct rtnl_neightbl *rtnl_neightbl_get(struct nl_cache *,
 					       const char *, int);
 extern void rtnl_neightbl_dump(struct rtnl_neightbl *, FILE *,
 			       struct nl_dump_params *);
 
-extern struct nl_msg *rtnl_neightbl_build_change_request(struct rtnl_neightbl *,
-						 struct rtnl_neightbl *);
-extern int rtnl_neightbl_change(struct nl_handle *, struct rtnl_neightbl *,
+extern int rtnl_neightbl_build_change_request(struct rtnl_neightbl *,
+					      struct rtnl_neightbl *,
+					      struct nl_msg **);
+extern int rtnl_neightbl_change(struct nl_sock *, struct rtnl_neightbl *,
 				struct rtnl_neightbl *);
 
 extern void rtnl_neightbl_set_family(struct rtnl_neightbl *, int);
diff --git a/include/netlink/route/nexthop.h b/include/netlink/route/nexthop.h
index 984f4b5..2aa44dc 100644
--- a/include/netlink/route/nexthop.h
+++ b/include/netlink/route/nexthop.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_ROUTE_NEXTHOP_H_
@@ -21,18 +21,42 @@
 
 struct rtnl_nexthop;
 
-extern struct rtnl_nexthop *	rtnl_route_nh_alloc(void);
-extern struct rtnl_nexthop *	rtnl_route_nh_clone(struct rtnl_nexthop *);
+enum {
+	NH_DUMP_FROM_ONELINE = -2,
+	NH_DUMP_FROM_DETAILS = -1,
+	NH_DUMP_FROM_ENV = 0,
+	/* > 0 reserved for nexthop index */
+};
+
+extern struct rtnl_nexthop * rtnl_route_nh_alloc(void);
+extern struct rtnl_nexthop * rtnl_route_nh_clone(struct rtnl_nexthop *);
 extern void		rtnl_route_nh_free(struct rtnl_nexthop *);
-extern void		rtnl_route_nh_set_weight(struct rtnl_nexthop *, int);
+
+extern int		rtnl_route_nh_compare(struct rtnl_nexthop *,
+					      struct rtnl_nexthop *,
+					      uint32_t, int);
+
+extern void		rtnl_route_nh_dump(struct rtnl_nexthop *,
+					   struct nl_dump_params *);
+
+extern void		rtnl_route_nh_set_weight(struct rtnl_nexthop *, uint8_t);
+extern uint8_t		rtnl_route_nh_get_weight(struct rtnl_nexthop *);
 extern void		rtnl_route_nh_set_ifindex(struct rtnl_nexthop *, int);
+extern int		rtnl_route_nh_get_ifindex(struct rtnl_nexthop *);
 extern void		rtnl_route_nh_set_gateway(struct rtnl_nexthop *,
 						  struct nl_addr *);
+extern struct nl_addr *	rtnl_route_nh_get_gateway(struct rtnl_nexthop *);
 extern void		rtnl_route_nh_set_flags(struct rtnl_nexthop *,
 						unsigned int);
 extern void		rtnl_route_nh_unset_flags(struct rtnl_nexthop *,
 						  unsigned int);
 extern unsigned int	rtnl_route_nh_get_flags(struct rtnl_nexthop *);
+extern void		rtnl_route_nh_set_realms(struct rtnl_nexthop *,
+						 uint32_t);
+extern uint32_t		rtnl_route_nh_get_realms(struct rtnl_nexthop *);
+
+extern char *		rtnl_route_nh_flags2str(int, char *, size_t);
+extern int		rtnl_route_nh_str2flags(const char *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/pktloc.h b/include/netlink/route/pktloc.h
new file mode 100644
index 0000000..28e1dc2
--- /dev/null
+++ b/include/netlink/route/pktloc.h
@@ -0,0 +1,44 @@
+/*
+ * netlink/route/pktloc.h         Packet Location Aliasing
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_PKTLOC_H_
+#define NETLINK_PKTLOC_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/route/tc.h>
+
+#include <linux/tc_ematch/tc_em_cmp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rtnl_pktloc
+{
+	char *			name;
+	uint8_t			align:4;
+	uint8_t			layer:4;
+	uint8_t			flags;
+	uint16_t		offset;
+	uint32_t		mask;
+
+	struct nl_list_head	list;
+};
+
+extern int rtnl_pktloc_lookup(const char *, struct rtnl_pktloc **);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/qdisc-modules.h b/include/netlink/route/qdisc-modules.h
index 802eac4..769625e 100644
--- a/include/netlink/route/qdisc-modules.h
+++ b/include/netlink/route/qdisc-modules.h
@@ -32,14 +32,16 @@
 	/**
 	 * Dump callbacks
 	 */
-	int  (*qo_dump[NL_DUMP_MAX+1])(struct rtnl_qdisc *,
-				       struct nl_dump_params *, int);
+	void  (*qo_dump[NL_DUMP_MAX+1])(struct rtnl_qdisc *,
+					struct nl_dump_params *);
 
 	/**
 	 * Must return the contents supposed to be in TCA_OPTIONS
 	 */
 	struct nl_msg *(*qo_get_opts)(struct rtnl_qdisc *);
 
+	int (*qo_build_msg)(struct rtnl_qdisc *, struct nl_msg *);
+
 	/**
 	 * TCA_OPTIONS message parser
 	 */
diff --git a/include/netlink/route/qdisc.h b/include/netlink/route/qdisc.h
index ee71304..5acd6e1 100644
--- a/include/netlink/route/qdisc.h
+++ b/include/netlink/route/qdisc.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_QDISC_H_
@@ -24,60 +24,46 @@
 
 extern struct nl_object_ops qdisc_obj_ops;
 
-/* General */
-extern struct rtnl_qdisc *	rtnl_qdisc_alloc(void);
-extern void			rtnl_qdisc_put(struct rtnl_qdisc *);
+extern struct rtnl_qdisc *rtnl_qdisc_alloc(void);
+extern void	rtnl_qdisc_put(struct rtnl_qdisc *);
 
-/* Cache Management */
-extern struct nl_cache *	rtnl_qdisc_alloc_cache(struct nl_handle *);
-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_alloc_cache(struct nl_sock *, struct nl_cache **);
+extern struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *, int, uint32_t);
+extern struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *,
+						   int, uint32_t);
 
-/* qdisc addition */
-extern struct nl_msg *	rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int);
-extern int		rtnl_qdisc_add(struct nl_handle *, struct rtnl_qdisc *,
-				       int);
+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);
 
-/* qdisc modification */
-extern struct nl_msg *	rtnl_qdisc_build_change_request(struct rtnl_qdisc *,
-							struct rtnl_qdisc *);
-extern int		rtnl_qdisc_change(struct nl_handle *,
-					  struct rtnl_qdisc *,
-					  struct rtnl_qdisc *);
+extern int	rtnl_qdisc_build_change_request(struct rtnl_qdisc *,
+						struct rtnl_qdisc *,
+						struct nl_msg **);
+extern int	rtnl_qdisc_change(struct nl_sock *, struct rtnl_qdisc *,
+				  struct rtnl_qdisc *);
 
-/* qdisc deletion */
-extern struct nl_msg *	rtnl_qdisc_build_delete_request(struct rtnl_qdisc *);
-extern int		rtnl_qdisc_delete(struct nl_handle *,
-					  struct rtnl_qdisc *);
+extern int	rtnl_qdisc_build_delete_request(struct rtnl_qdisc *,
+						struct nl_msg **);
+extern int	rtnl_qdisc_delete(struct nl_sock *, struct rtnl_qdisc *);
 
-/* attribute modifications */
-extern void		rtnl_qdisc_set_ifindex(struct rtnl_qdisc *, int);
-extern int		rtnl_qdisc_get_ifindex(struct rtnl_qdisc *);
-extern void		rtnl_qdisc_set_handle(struct rtnl_qdisc *, uint32_t);
-extern uint32_t		rtnl_qdisc_get_handle(struct rtnl_qdisc *);
-extern void		rtnl_qdisc_set_parent(struct rtnl_qdisc *, uint32_t);
-extern uint32_t		rtnl_qdisc_get_parent(struct rtnl_qdisc *);
-extern void		rtnl_qdisc_set_kind(struct rtnl_qdisc *, const char *);
-extern char *		rtnl_qdisc_get_kind(struct rtnl_qdisc *);
-extern uint64_t		rtnl_qdisc_get_stat(struct rtnl_qdisc *,
-					    enum rtnl_tc_stats_id);
+extern void	rtnl_qdisc_set_ifindex(struct rtnl_qdisc *, int);
+extern int	rtnl_qdisc_get_ifindex(struct rtnl_qdisc *);
+extern void	rtnl_qdisc_set_handle(struct rtnl_qdisc *, uint32_t);
+extern uint32_t	rtnl_qdisc_get_handle(struct rtnl_qdisc *);
+extern void	rtnl_qdisc_set_parent(struct rtnl_qdisc *, uint32_t);
+extern uint32_t	rtnl_qdisc_get_parent(struct rtnl_qdisc *);
+extern void	rtnl_qdisc_set_kind(struct rtnl_qdisc *, const char *);
+extern char *	rtnl_qdisc_get_kind(struct rtnl_qdisc *);
+extern uint64_t	rtnl_qdisc_get_stat(struct rtnl_qdisc *, enum rtnl_tc_stats_id);
 
-/* iterators */
-extern void		rtnl_qdisc_foreach_child(struct rtnl_qdisc *,
-						 struct nl_cache *,
-						 void (*cb)(struct nl_object *,
-							    void *),
-						 void *);
+extern void	rtnl_qdisc_foreach_child(struct rtnl_qdisc *, struct nl_cache *,
+					 void (*cb)(struct nl_object *, void *),
+					 void *);
 
-extern void		rtnl_qdisc_foreach_cls(struct rtnl_qdisc *,
-					       struct nl_cache *,
-					       void (*cb)(struct nl_object *,
-							  void *),
-					       void *);
+extern void	rtnl_qdisc_foreach_cls(struct rtnl_qdisc *, struct nl_cache *,
+				       void (*cb)(struct nl_object *, void *),
+				       void *);
 
-/* qdisc specific options */
 extern struct nl_msg *	rtnl_qdisc_get_opts(struct rtnl_qdisc *);
 
 #ifdef __cplusplus
diff --git a/include/netlink/route/route.h b/include/netlink/route/route.h
index f59f36b..5729cd7 100644
--- a/include/netlink/route/route.h
+++ b/include/netlink/route/route.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_ROUTE_H_
@@ -17,11 +17,16 @@
 #include <netlink/addr.h>
 #include <netlink/data.h>
 #include <netlink/route/nexthop.h>
+#include <netlink/route/rtnl.h>
+#include <linux/in_route.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* flags */
+#define ROUTE_CACHE_CONTENT	1
+
 struct rtnl_route;
 
 struct rtnl_rtcacheinfo
@@ -38,85 +43,79 @@
 
 extern struct nl_object_ops route_obj_ops;
 
-/* General */
 extern struct rtnl_route *	rtnl_route_alloc(void);
-extern void			rtnl_route_put(struct rtnl_route *);
-extern struct nl_cache *	rtnl_route_alloc_cache(struct nl_handle *);
+extern void	rtnl_route_put(struct rtnl_route *);
+extern int	rtnl_route_alloc_cache(struct nl_sock *, int, int,
+				       struct nl_cache **);
 
-extern void		rtnl_route_get(struct rtnl_route *);
-extern void		rtnl_route_put(struct rtnl_route *);
+extern void	rtnl_route_get(struct rtnl_route *);
+extern void	rtnl_route_put(struct rtnl_route *);
 
-extern struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *, int);
-extern int rtnl_route_add(struct nl_handle *, struct rtnl_route *, int);
-extern struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *, int);
-extern int rtnl_route_del(struct nl_handle *, struct rtnl_route *, int);
+extern int	rtnl_route_parse(struct nlmsghdr *, struct rtnl_route **);
+extern int	rtnl_route_build_msg(struct nl_msg *, struct rtnl_route *);
 
-extern void		rtnl_route_set_table(struct rtnl_route *, int);
-extern int		rtnl_route_get_table(struct rtnl_route *);
-extern void		rtnl_route_set_scope(struct rtnl_route *, int);
-extern int		rtnl_route_get_scope(struct rtnl_route *);
-extern void		rtnl_route_set_tos(struct rtnl_route *, int);
-extern int		rtnl_route_get_tos(struct rtnl_route *);
-extern void		rtnl_route_set_realms(struct rtnl_route *, realm_t);
-extern realm_t		rtnl_route_get_realms(struct rtnl_route *);
-extern void		rtnl_route_set_protocol(struct rtnl_route *, int);
-extern int		rtnl_route_get_protocol(struct rtnl_route *);
-extern void		rtnl_route_set_prio(struct rtnl_route *, int);
-extern int		rtnl_route_get_prio(struct rtnl_route *);
-extern void		rtnl_route_set_family(struct rtnl_route *, int);
-extern int		rtnl_route_get_family(struct rtnl_route *);
-extern void		rtnl_route_set_type(struct rtnl_route *, int);
-extern int		rtnl_route_get_type(struct rtnl_route *);
-extern void		rtnl_route_set_flags(struct rtnl_route *,
-					     unsigned int);
-extern void		rtnl_route_unset_flags(struct rtnl_route *,
-					       unsigned int);
-extern unsigned int	rtnl_route_get_flags(struct rtnl_route *);
-extern int		rtnl_route_set_metric(struct rtnl_route *, int,
-					      unsigned int);
-extern int		rtnl_route_unset_metric(struct rtnl_route *, int);
-extern unsigned int	rtnl_route_get_metric(struct rtnl_route *, int);
-extern int		rtnl_route_set_dst(struct rtnl_route *,
-					   struct nl_addr *);
-extern struct nl_addr *	rtnl_route_get_dst(struct rtnl_route *);
-extern int		rtnl_route_set_src(struct rtnl_route *,
-					   struct nl_addr *);
-extern struct nl_addr *	rtnl_route_get_src(struct rtnl_route *);
-extern int		rtnl_route_set_gateway(struct rtnl_route *,
-					       struct nl_addr *);
-extern struct nl_addr *	rtnl_route_get_gateway(struct rtnl_route *);
-extern int		rtnl_route_set_pref_src(struct rtnl_route *,
-						struct nl_addr *);
-extern struct nl_addr *	rtnl_route_get_pref_src(struct rtnl_route *);
-extern void		rtnl_route_set_oif(struct rtnl_route *, int);
-extern int		rtnl_route_get_oif(struct rtnl_route *);
-extern void		rtnl_route_set_iif(struct rtnl_route *, const char *);
-extern char *		rtnl_route_get_iif(struct rtnl_route *);
-extern int		rtnl_route_get_dst_len(struct rtnl_route *);
-extern int		rtnl_route_get_src_len(struct rtnl_route *);
+extern int	rtnl_route_build_add_request(struct rtnl_route *, int,
+					     struct nl_msg **);
+extern int	rtnl_route_add(struct nl_sock *, struct rtnl_route *, int);
+extern int	rtnl_route_build_del_request(struct rtnl_route *, int,
+					     struct nl_msg **);
+extern int	rtnl_route_delete(struct nl_sock *, struct rtnl_route *, int);
 
-extern void		rtnl_route_add_nexthop(struct rtnl_route *,
-					       struct rtnl_nexthop *);
-extern void		rtnl_route_remove_nexthop(struct rtnl_nexthop *);
-extern struct nl_list_head *	rtnl_route_get_nexthops(struct rtnl_route *);
-extern void		rtnl_route_set_cacheinfo(struct rtnl_route *,
-						 struct rtnl_rtcacheinfo *);
-extern uint32_t		rtnl_route_get_mp_algo(struct rtnl_route *);
-extern void		rtnl_route_set_mp_algo(struct rtnl_route *, uint32_t);
+extern void	rtnl_route_set_table(struct rtnl_route *, uint32_t);
+extern uint32_t	rtnl_route_get_table(struct rtnl_route *);
+extern void	rtnl_route_set_scope(struct rtnl_route *, uint8_t);
+extern uint8_t	rtnl_route_get_scope(struct rtnl_route *);
+extern void	rtnl_route_set_tos(struct rtnl_route *, uint8_t);
+extern uint8_t	rtnl_route_get_tos(struct rtnl_route *);
+extern void	rtnl_route_set_protocol(struct rtnl_route *, uint8_t);
+extern uint8_t	rtnl_route_get_protocol(struct rtnl_route *);
+extern void	rtnl_route_set_priority(struct rtnl_route *, uint32_t);
+extern uint32_t	rtnl_route_get_priority(struct rtnl_route *);
+extern int	rtnl_route_set_family(struct rtnl_route *, uint8_t);
+extern uint8_t	rtnl_route_get_family(struct rtnl_route *);
+extern int	rtnl_route_set_type(struct rtnl_route *, uint8_t);
+extern uint8_t	rtnl_route_get_type(struct rtnl_route *);
+extern void	rtnl_route_set_flags(struct rtnl_route *, uint32_t);
+extern void	rtnl_route_unset_flags(struct rtnl_route *, uint32_t);
+extern uint32_t	rtnl_route_get_flags(struct rtnl_route *);
+extern int	rtnl_route_set_metric(struct rtnl_route *, int, unsigned int);
+extern int	rtnl_route_unset_metric(struct rtnl_route *, int);
+extern int	rtnl_route_get_metric(struct rtnl_route *, int, uint32_t *);
+extern int	rtnl_route_set_dst(struct rtnl_route *, struct nl_addr *);
+extern struct nl_addr *rtnl_route_get_dst(struct rtnl_route *);
+extern int	rtnl_route_set_src(struct rtnl_route *, struct nl_addr *);
+extern struct nl_addr *rtnl_route_get_src(struct rtnl_route *);
+extern int	rtnl_route_set_pref_src(struct rtnl_route *, struct nl_addr *);
+extern struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *);
+extern void	rtnl_route_set_iif(struct rtnl_route *, int);
+extern int	rtnl_route_get_iif(struct rtnl_route *);
+extern int	rtnl_route_get_src_len(struct rtnl_route *);
 
-extern char *		rtnl_route_table2str(int, char *, size_t);
-extern int		rtnl_route_str2table(const char *);
-extern int		rtnl_route_read_table_names(const char *);
+extern void	rtnl_route_add_nexthop(struct rtnl_route *,
+				       struct rtnl_nexthop *);
+extern void	rtnl_route_remove_nexthop(struct rtnl_route *,
+					  struct rtnl_nexthop *);
+extern struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *);
+extern int	rtnl_route_get_nnexthops(struct rtnl_route *);
 
-extern char *		rtnl_route_proto2str(int, char *, size_t);
-extern int		rtnl_route_str2proto(const char *);
-extern int		rtnl_route_read_protocol_names(const char *);
+extern void	rtnl_route_foreach_nexthop(struct rtnl_route *r,
+                                 void (*cb)(struct rtnl_nexthop *, void *),
+                                 void *arg);
 
-extern char *		rtnl_route_metric2str(int, char *, size_t);
-extern int		rtnl_route_str2metric(const char *);
+extern struct rtnl_nexthop * rtnl_route_nexthop_n(struct rtnl_route *r, int n);
 
-extern char *		rtnl_route_nh_flags2str(int, char *, size_t);
-extern int		rtnl_route_nh_str2flags(const char *);
+extern int	rtnl_route_guess_scope(struct rtnl_route *);
+
+extern char *	rtnl_route_table2str(int, char *, size_t);
+extern int	rtnl_route_str2table(const char *);
+extern int	rtnl_route_read_table_names(const char *);
+
+extern char *	rtnl_route_proto2str(int, char *, size_t);
+extern int	rtnl_route_str2proto(const char *);
+extern int	rtnl_route_read_protocol_names(const char *);
+
+extern char *	rtnl_route_metric2str(int, char *, size_t);
+extern int	rtnl_route_str2metric(const char *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/rtnl.h b/include/netlink/route/rtnl.h
index 9d116cd..f551a5d 100644
--- a/include/netlink/route/rtnl.h
+++ b/include/netlink/route/rtnl.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_RTNL_H_
@@ -23,8 +23,6 @@
  * @{
  */
 
-typedef uint32_t	realm_t;
-
 /**
  * Mask specying the size of each realm part
  * @ingroup rtnl
@@ -51,7 +49,7 @@
 
 
 /* General */
-extern int		nl_rtgen_request(struct nl_handle *, int, int, int);
+extern int		nl_rtgen_request(struct nl_sock *, int, int, int);
 
 /* Routing Type Translations */
 extern char *		nl_rtntype2str(int, char *, size_t);
diff --git a/include/netlink/route/rule.h b/include/netlink/route/rule.h
index d295b0d..928dc0f 100644
--- a/include/netlink/route/rule.h
+++ b/include/netlink/route/rule.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_RULE_H_
@@ -27,15 +27,16 @@
 extern struct rtnl_rule *	rtnl_rule_alloc(void);
 extern void			rtnl_rule_put(struct rtnl_rule *);
 
-extern struct nl_cache * rtnl_rule_alloc_cache(struct nl_handle *);
-extern struct nl_cache * rtnl_rule_alloc_cache_by_family(struct nl_handle *,
-							 int);
+extern int	rtnl_rule_alloc_cache(struct nl_sock *, int,
+				      struct nl_cache **);
 extern void rtnl_rule_dump(struct rtnl_rule *, FILE *, struct nl_dump_params *);
 
-extern struct nl_msg * rtnl_rule_build_add_request(struct rtnl_rule *, int);
-extern int rtnl_rule_add(struct nl_handle *, struct rtnl_rule *, int);
-extern struct nl_msg * rtnl_rule_build_delete_request(struct rtnl_rule *, int);
-extern int rtnl_rule_delete(struct nl_handle *, struct rtnl_rule *, int);
+extern int	rtnl_rule_build_add_request(struct rtnl_rule *, int,
+					    struct nl_msg **);
+extern int rtnl_rule_add(struct nl_sock *, struct rtnl_rule *, int);
+extern int	rtnl_rule_build_delete_request(struct rtnl_rule *, int,
+					       struct nl_msg **);
+extern int rtnl_rule_delete(struct nl_sock *, struct rtnl_rule *, int);
 
 
 /* attribute modification */
@@ -43,9 +44,7 @@
 extern int		rtnl_rule_get_family(struct rtnl_rule *);
 extern void		rtnl_rule_set_prio(struct rtnl_rule *, int);
 extern int		rtnl_rule_get_prio(struct rtnl_rule *);
-#define rtnl_rule_set_fwmark(ptr, n) rtnl_rule_set_mark(ptr, n)
 extern void		rtnl_rule_set_mark(struct rtnl_rule *, uint64_t);
-#define rtnl_rule_get_fwmark(ptr) rtnl_rule_get_mark(ptr)
 extern uint64_t		rtnl_rule_get_mark(struct rtnl_rule *);
 extern void		rtnl_rule_set_table(struct rtnl_rule *, int);
 extern int		rtnl_rule_get_table(struct rtnl_rule *);
@@ -69,8 +68,8 @@
 extern void		rtnl_rule_set_classid(struct rtnl_rule *, uint32_t);
 extern uint32_t		rtnl_rule_get_classid(struct rtnl_rule *);
 
-extern void		rtnl_rule_set_realms(struct rtnl_rule *, realm_t);
-extern realm_t		rtnl_rule_get_realms(struct rtnl_rule *);
+extern void		rtnl_rule_set_realms(struct rtnl_rule *, uint32_t);
+extern uint32_t		rtnl_rule_get_realms(struct rtnl_rule *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/sch/htb.h b/include/netlink/route/sch/htb.h
index 5d4d681..d44f039 100644
--- a/include/netlink/route/sch/htb.h
+++ b/include/netlink/route/sch/htb.h
@@ -30,9 +30,9 @@
 extern void rtnl_htb_set_ceil(struct rtnl_class *, uint32_t);
 extern void rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t);
 extern void rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t);
-extern void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum);
-extern void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead);
-extern void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu);
+extern void rtnl_htb_set_quantum(struct rtnl_class *, uint32_t quantum);
+extern void rtnl_htb_set_overhead(struct rtnl_class *, uint8_t overhead);
+extern void rtnl_htb_set_mpu(struct rtnl_class *, uint8_t mpu);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/route/sch/netem.h b/include/netlink/route/sch/netem.h
index b100741..c293777 100644
--- a/include/netlink/route/sch/netem.h
+++ b/include/netlink/route/sch/netem.h
@@ -31,6 +31,13 @@
 extern int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *, int);
 extern int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *);
 
+/* Corruption */
+extern int rtnl_netem_set_corruption_probability(struct rtnl_qdisc *, int);
+extern int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *);
+
+extern int rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *, int);
+extern int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *);
+
 /* Packet Loss */
 extern int rtnl_netem_set_loss(struct rtnl_qdisc *, int);
 extern int rtnl_netem_get_loss(struct rtnl_qdisc *);
@@ -55,6 +62,12 @@
 extern int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *, int);
 extern int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *);
 
+/* Delay Distribution */
+#define MAXDIST 65536
+extern int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *, const char *);
+extern int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *);
+extern int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *, int16_t **);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/netlink/socket.h b/include/netlink/socket.h
index 038df7a..7e71aed 100644
--- a/include/netlink/socket.h
+++ b/include/netlink/socket.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_SOCKET_H_
@@ -19,45 +19,45 @@
 extern "C" {
 #endif
 
-extern struct nl_handle *	nl_handle_alloc(void);
-extern struct nl_handle *	nl_handle_alloc_cb(struct nl_cb *);
-extern void			nl_handle_destroy(struct nl_handle *);
+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(struct nl_handle *);
-extern void			nl_socket_set_local_port(struct nl_handle *,
-							 uint32_t);
+extern uint32_t		nl_socket_get_local_port(struct nl_sock *);
+extern void		nl_socket_set_local_port(struct nl_sock *, uint32_t);
 
-extern int			nl_socket_add_membership(struct nl_handle *,
-							 int);
-extern int			nl_socket_drop_membership(struct nl_handle *,
+extern int		nl_socket_add_memberships(struct nl_sock *, int, ...);
+extern int		nl_socket_add_membership(struct nl_sock *, int);
+extern int		nl_socket_drop_memberships(struct nl_sock *, int, ...);
+extern int		nl_socket_drop_membership(struct nl_sock *,
 							  int);
-extern void			nl_join_groups(struct nl_handle *, int);
+extern void		nl_join_groups(struct nl_sock *, int);
 
-extern uint32_t			nl_socket_get_peer_port(struct nl_handle *);
-extern void			nl_socket_set_peer_port(struct nl_handle *,
+
+extern uint32_t		nl_socket_get_peer_port(struct nl_sock *);
+extern void		nl_socket_set_peer_port(struct nl_sock *,
 							uint32_t);
 
-extern struct nl_cb *		nl_socket_get_cb(struct nl_handle *);
-extern void			nl_socket_set_cb(struct nl_handle *,
+extern struct nl_cb *	nl_socket_get_cb(struct nl_sock *);
+extern void		nl_socket_set_cb(struct nl_sock *,
 						 struct nl_cb *);
-extern int			nl_socket_modify_cb(struct nl_handle *,
-						    enum nl_cb_type,
-						    enum nl_cb_kind,
-						    nl_recvmsg_msg_cb_t,
-						    void *);
+extern int		nl_socket_modify_cb(struct nl_sock *, enum nl_cb_type,
+					    enum nl_cb_kind,
+					    nl_recvmsg_msg_cb_t, void *);
 
-extern int			nl_set_buffer_size(struct nl_handle *,
-						   int, int);
-extern int			nl_set_passcred(struct nl_handle *, int);
-extern int			nl_socket_recv_pktinfo(struct nl_handle *, int);
+extern int		nl_socket_set_buffer_size(struct nl_sock *, int, int);
+extern int		nl_socket_set_passcred(struct nl_sock *, int);
+extern int		nl_socket_recv_pktinfo(struct nl_sock *, int);
 
-extern void			nl_disable_sequence_check(struct nl_handle *);
-extern unsigned int		nl_socket_use_seq(struct nl_handle *);
+extern void		nl_socket_disable_seq_check(struct nl_sock *);
+extern unsigned int	nl_socket_use_seq(struct nl_sock *);
+extern void		nl_socket_disable_auto_ack(struct nl_sock *);
+extern void		nl_socket_enable_auto_ack(struct nl_sock *);
 
-extern int			nl_socket_get_fd(struct nl_handle *);
-extern int			nl_socket_set_nonblocking(struct nl_handle *);
-extern void			nl_socket_enable_msg_peek(struct nl_handle *);
-extern void			nl_socket_disable_msg_peek(struct nl_handle *);
+extern int		nl_socket_get_fd(struct nl_sock *);
+extern int		nl_socket_set_nonblocking(struct nl_sock *);
+extern void		nl_socket_enable_msg_peek(struct nl_sock *);
+extern void		nl_socket_disable_msg_peek(struct nl_sock *);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/types.h b/include/netlink/types.h
index 903028e..2e0b9c3 100644
--- a/include/netlink/types.h
+++ b/include/netlink/types.h
@@ -19,12 +19,10 @@
  * @ingroup utils
  */
 enum nl_dump_type {
-	NL_DUMP_BRIEF,		/**< Dump object in a brief one-liner */
-	NL_DUMP_FULL,		/**< Dump all attributes but no statistics */
+	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_XML,		/**< Dump all attribtes in XML format */
 	NL_DUMP_ENV,		/**< Dump all attribtues as env variables */
-	NL_DUMP_EVENTS,		/**< Dump event */
 	__NL_DUMP_MAX,
 };
 #define NL_DUMP_MAX (__NL_DUMP_MAX - 1)
@@ -100,6 +98,14 @@
 	 * 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;
 };
 
 #endif
diff --git a/include/netlink/utils.h b/include/netlink/utils.h
index 0351d38..480bab6 100644
--- a/include/netlink/utils.h
+++ b/include/netlink/utils.h
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #ifndef NETLINK_UTILS_H_
@@ -38,10 +38,6 @@
 
 /** @} */
 
-extern char *	nl_geterror(void);
-extern int	nl_get_errno(void);
-extern void	nl_perror(const char *);
-
 /* unit pretty-printing */
 extern double	nl_cancel_down_bytes(unsigned long long, char **);
 extern double	nl_cancel_down_bits(unsigned long long, char **);
@@ -55,6 +51,7 @@
 extern int	nl_get_hz(void);
 extern uint32_t	nl_us2ticks(uint32_t);
 extern uint32_t	nl_ticks2us(uint32_t);
+extern int	nl_str2msec(const char *, uint64_t *);
 extern char *	nl_msec2str(uint64_t, char *, size_t);
 
 /* link layer protocol translations */
@@ -70,9 +67,9 @@
 extern int	nl_str2ip_proto(const char *);
 
 /* Dumping helpers */
-extern void	nl_new_line(struct nl_dump_params *, int);
+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 *, int, const char *, ...);
+extern void	nl_dump_line(struct nl_dump_params *, const char *, ...);
 
 #ifdef __cplusplus
 }
diff --git a/include/netlink/version.h b/include/netlink/version.h
new file mode 100644
index 0000000..84af8f3
--- /dev/null
+++ b/include/netlink/version.h
@@ -0,0 +1,18 @@
+/*
+ * netlink/version.h	Compile Time Versioning Information
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_VERSION_H_
+#define NETLINK_VERSION_H_
+
+#define LIBNL_STRING "libnl 2.0"
+#define LIBNL_VERSION "2.0"
+
+#endif
diff --git a/include/netlink/version.h.in b/include/netlink/version.h.in
new file mode 100644
index 0000000..7bd38cc
--- /dev/null
+++ b/include/netlink/version.h.in
@@ -0,0 +1,18 @@
+/*
+ * netlink/version.h	Compile Time Versioning Information
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_VERSION_H_
+#define NETLINK_VERSION_H_
+
+#define LIBNL_STRING "@PACKAGE_STRING@"
+#define LIBNL_VERSION "@PACKAGE_VERSION@"
+
+#endif
diff --git a/install-sh b/install-sh
deleted file mode 100755
index 6ce63b9..0000000
--- a/install-sh
+++ /dev/null
@@ -1,294 +0,0 @@
-#!/bin/sh
-#
-# install - install a program, script, or datafile
-#
-# This originates from X11R5 (mit/util/scripts/install.sh), which was
-# later released in X11R6 (xc/config/util/install.sh) with the
-# following copyright and license.
-#
-# Copyright (C) 1994 X Consortium
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
-# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
-# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-# Except as contained in this notice, the name of the X Consortium shall not
-# be used in advertising or otherwise to promote the sale, use or other deal-
-# ings in this Software without prior written authorization from the X Consor-
-# tium.
-#
-#
-# FSF changes to this file are in the public domain.
-#
-# Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
-# when there is no Makefile.
-#
-# This script is compatible with the BSD install script, but was written
-# from scratch.  It can only install one file at a time, a restriction
-# shared with many OS's install programs.
-
-
-# set DOITPROG to echo to test this script
-
-# Don't use :- since 4.3BSD and earlier shells don't like it.
-doit="${DOITPROG-}"
-
-
-# put in absolute paths if you don't have them in your path; or use env. vars.
-
-mvprog="${MVPROG-mv}"
-cpprog="${CPPROG-cp}"
-chmodprog="${CHMODPROG-chmod}"
-chownprog="${CHOWNPROG-chown}"
-chgrpprog="${CHGRPPROG-chgrp}"
-stripprog="${STRIPPROG-strip}"
-rmprog="${RMPROG-rm}"
-mkdirprog="${MKDIRPROG-mkdir}"
-
-transformbasename=""
-transform_arg=""
-instcmd="$mvprog"
-chmodcmd="$chmodprog 0755"
-chowncmd=""
-chgrpcmd=""
-stripcmd=""
-rmcmd="$rmprog -f"
-mvcmd="$mvprog"
-src=""
-dst=""
-dir_arg=""
-
-while [ x"$1" != x ]; do
-    case $1 in
-	-c) instcmd=$cpprog
-	    shift
-	    continue;;
-
-	-d) dir_arg=true
-	    shift
-	    continue;;
-
-	-m) chmodcmd="$chmodprog $2"
-	    shift
-	    shift
-	    continue;;
-
-	-o) chowncmd="$chownprog $2"
-	    shift
-	    shift
-	    continue;;
-
-	-g) chgrpcmd="$chgrpprog $2"
-	    shift
-	    shift
-	    continue;;
-
-	-s) stripcmd=$stripprog
-	    shift
-	    continue;;
-
-	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
-	    shift
-	    continue;;
-
-	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
-	    shift
-	    continue;;
-
-	*)  if [ x"$src" = x ]
-	    then
-		src=$1
-	    else
-		# this colon is to work around a 386BSD /bin/sh bug
-		:
-		dst=$1
-	    fi
-	    shift
-	    continue;;
-    esac
-done
-
-if [ x"$src" = x ]
-then
-	echo "$0: no input file specified" >&2
-	exit 1
-else
-	:
-fi
-
-if [ x"$dir_arg" != x ]; then
-	dst=$src
-	src=""
-
-	if [ -d "$dst" ]; then
-		instcmd=:
-		chmodcmd=""
-	else
-		instcmd=$mkdirprog
-	fi
-else
-
-# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
-# might cause directories to be created, which would be especially bad
-# if $src (and thus $dsttmp) contains '*'.
-
-	if [ -f "$src" ] || [ -d "$src" ]
-	then
-		:
-	else
-		echo "$0: $src does not exist" >&2
-		exit 1
-	fi
-
-	if [ x"$dst" = x ]
-	then
-		echo "$0: no destination specified" >&2
-		exit 1
-	else
-		:
-	fi
-
-# If destination is a directory, append the input filename; if your system
-# does not like double slashes in filenames, you may need to add some logic
-
-	if [ -d "$dst" ]
-	then
-		dst=$dst/`basename "$src"`
-	else
-		:
-	fi
-fi
-
-## this sed command emulates the dirname command
-dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
-
-# Make sure that the destination directory exists.
-#  this part is taken from Noah Friedman's mkinstalldirs script
-
-# Skip lots of stat calls in the usual case.
-if [ ! -d "$dstdir" ]; then
-defaultIFS='
-	'
-IFS="${IFS-$defaultIFS}"
-
-oIFS=$IFS
-# Some sh's can't handle IFS=/ for some reason.
-IFS='%'
-set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
-IFS=$oIFS
-
-pathcomp=''
-
-while [ $# -ne 0 ] ; do
-	pathcomp=$pathcomp$1
-	shift
-
-	if [ ! -d "$pathcomp" ] ;
-        then
-		$mkdirprog "$pathcomp"
-	else
-		:
-	fi
-
-	pathcomp=$pathcomp/
-done
-fi
-
-if [ x"$dir_arg" != x ]
-then
-	$doit $instcmd "$dst" &&
-
-	if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dst"; else : ; fi &&
-	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dst"; else : ; fi &&
-	if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dst"; else : ; fi &&
-	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dst"; else : ; fi
-else
-
-# If we're going to rename the final executable, determine the name now.
-
-	if [ x"$transformarg" = x ]
-	then
-		dstfile=`basename "$dst"`
-	else
-		dstfile=`basename "$dst" $transformbasename |
-			sed $transformarg`$transformbasename
-	fi
-
-# don't allow the sed command to completely eliminate the filename
-
-	if [ x"$dstfile" = x ]
-	then
-		dstfile=`basename "$dst"`
-	else
-		:
-	fi
-
-# Make a couple of temp file names in the proper directory.
-
-	dsttmp=$dstdir/_inst.$$_
-	rmtmp=$dstdir/_rm.$$_
-
-# Trap to clean up temp files at exit.
-
-	trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0
-	trap '(exit $?); exit' 1 2 13 15
-
-# Move or copy the file name to the temp name
-
-	$doit $instcmd "$src" "$dsttmp" &&
-
-# and set any options; do chmod last to preserve setuid bits
-
-# If any of these fail, we abort the whole thing.  If we want to
-# ignore errors from any of these, just make sure not to ignore
-# errors from the above "$doit $instcmd $src $dsttmp" command.
-
-	if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else :;fi &&
-	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else :;fi &&
-	if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dsttmp"; else :;fi &&
-	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else :;fi &&
-
-# Now remove or move aside any old file at destination location.  We try this
-# two ways since rm can't unlink itself on some systems and the destination
-# file might be busy for other reasons.  In this case, the final cleanup
-# might fail but the new file should still install successfully.
-
-{
-	if [ -f "$dstdir/$dstfile" ]
-	then
-		$doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null ||
-		$doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null ||
-		{
-		  echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
-		  (exit 1); exit
-		}
-	else
-		:
-	fi
-} &&
-
-# Now rename the file to the real destination.
-
-	$doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
-
-fi &&
-
-# The final little trick to "correctly" pass the exit status to the exit trap.
-
-{
-	(exit 0); exit
-}
diff --git a/lib/.gitignore b/lib/.gitignore
index f4bf2cd..2a450e8 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -1 +1,2 @@
 libnl.so*
+libnl-*.so*
diff --git a/lib/Makefile b/lib/Makefile
deleted file mode 100644
index 0bf8af7..0000000
--- a/lib/Makefile
+++ /dev/null
@@ -1,76 +0,0 @@
-#
-# lib/Makefile
-#
-# 	This library is free software; you can redistribute it and/or
-#	modify it under the terms of the GNU Lesser General Public
-#	License as published by the Free Software Foundation version 2.1
-#	of the License.
-#
-# Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
-#
-
-ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),)
-    include ../Makefile.opts
-endif
-
-# Core
-CIN      := $(wildcard *.c)
-# NETLINK_ROUTE
-CIN      += $(wildcard route/*.c)
-# Schedulers
-CIN      += $(wildcard route/sch/*.c)
-# Classifiers
-CIN      += $(wildcard route/cls/*.c)
-# Link Info Modules
-CIN      += $(wildcard route/link/*.c)
-# NETLINK_GENERIC
-CIN      += $(wildcard genl/*.c)
-# fib lookup
-CIN      += $(wildcard fib_lookup/*.c)
-# Netfilter
-CIN      += $(wildcard netfilter/*.c)
-
-DEPS     := $(CIN:%.c=%.d)
-OBJ      := $(CIN:%.c=%.o)
-CFLAGS   += -fPIC
-OUT_SLIB := $(PACKAGE_NAME).so.$(PACKAGE_VERSION)
-LN_SLIB  := $(PACKAGE_NAME).so
-LN1_SLIB := $(LN_SLIB).1
-
-export
-
-.PHONY: all clean install librtn.a $(OUT_SLIB)
-
-
-all:
-	@echo "  MAKE $(OUT_SLIB)"; \
-	$(MAKE) $(OUT_SLIB)
-
-$(OUT_SLIB): ../Makefile.opts $(OBJ)
-	@echo "  LD $(OUT_SLIB)"; \
-	$(CC) -shared -Wl,-soname,libnl.so.1 -o $(OUT_SLIB) $(OBJ) $(LIBNL_LIB) -lc
-	@echo "  LN $(OUT_SLIB) $(LN1_SLIB)"; \
-	rm -f $(LN1_SLIB) ; $(LN) -s $(OUT_SLIB) $(LN1_SLIB)
-	@echo "  LN $(LN1_SLIB) $(LN_SLIB)"; \
-	rm -f $(LN_SLIB) ; $(LN) -s $(LN1_SLIB) $(LN_SLIB)
-
-clean:
-	@echo "  CLEAN lib"; \
-	$(RM) -f $(OBJ) $(OUT_SLIB) $(LN_SLIB) $(LN1_SLIB); \
-	$(RM) -f $(DEPS) $(OUT_SLIB) $(LN_SLIB) $(LN1_SLIB)
-
-distclean:
-	@echo "  DISTCLEAN lib"; \
-	$(RM) -f $(DEPS)
-
-install:
-	mkdir -p $(DESTDIR)$(libdir)/
-	install -m 0644 $(OUT_SLIB) $(DESTDIR)$(libdir)
-	rm -f $(DESTDIR)$(libdir)/$(LN1_SLIB)
-	$(LN) -s $(OUT_SLIB) $(DESTDIR)$(libdir)/$(LN1_SLIB)
-	rm -f $(DESTDIR)$(libdir)/$(LN_SLIB)
-	$(LN) -s $(LN1_SLIB) $(DESTDIR)$(libdir)/$(LN_SLIB)
-
-$(DEPS): ../Makefile.opts
-
-include ../Makefile.rules
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..92a916e
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,57 @@
+# -*- Makefile -*-
+
+AM_CPPFLAGS  = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\"
+
+lib_LTLIBRARIES = \
+	libnl.la libnl-genl.la libnl-route.la libnl-nf.la
+
+libnl_la_LDFLAGS = -version-info 2:0:0
+libnl_la_SOURCES = \
+	addr.c attr.c cache.c cache_mngr.c cache_mngt.c data.c doc.c \
+	error.c handlers.c msg.c nl.c object.c socket.c utils.c
+
+libnl_genl_la_LDFLAGS = -version-info 2:0:0
+libnl_genl_la_LIBADD  = libnl.la
+libnl_genl_la_SOURCES = \
+	genl/ctrl.c genl/family.c genl/genl.c genl/mngt.c
+
+libnl_nf_la_LDFLAGS = -version-info 2:0:0
+libnl_nf_la_LIBADD  = libnl-route.la
+libnl_nf_la_SOURCES = \
+	netfilter/ct.c netfilter/ct_obj.c netfilter/log.c \
+	netfilter/log_msg.c netfilter/log_msg_obj.c netfilter/log_obj.c \
+	netfilter/netfilter.c netfilter/nfnl.c netfilter/queue.c \
+	netfilter/queue_msg.c netfilter/queue_msg_obj.c netfilter/queue_obj.c
+
+CLEANFILES = \
+	route/pktloc_grammar.c route/pktloc_grammar.h \
+	route/pktloc_syntax.c route/pktloc_syntax.h
+
+# Hack to avoid using ylwrap. It does not function correctly in combination
+# with --header-file=
+route/pktloc_grammar.c: route/pktloc_grammar.l
+	$(LEX) --header-file=route/pktloc_grammar.h $(LFLAGS) -o $@ $^
+
+route/pktloc_syntax.c: route/pktloc_syntax.y
+	$(YACC) -d $(YFLAGS) -o $@ $^
+
+libnl_route_la_LDFLAGS = -version-info 2:0:0
+libnl_route_la_LIBADD  = libnl.la
+libnl_route_la_SOURCES = \
+	route/addr.c route/class.c route/class_api.c route/class_obj.c \
+	route/cls.c route/cls_api.c route/cls_obj.c route/link.c \
+	route/neigh.c route/neightbl.c route/nexthop.c route/qdisc.c \
+	route/qdisc_api.c route/qdisc_obj.c route/route.c route/route_obj.c \
+	route/route_utils.c route/rtnl.c route/rule.c route/tc.c \
+	\
+	route/cls/fw.c route/cls/police.c route/cls/u32.c \
+	\
+	route/link/api.c route/link/vlan.c \
+	\
+	route/sch/blackhole.c route/sch/cbq.c route/sch/dsmark.c \
+	route/sch/fifo.c route/sch/htb.c route/sch/netem.c route/sch/prio.c \
+	route/sch/red.c route/sch/sfq.c route/sch/tbf.c \
+	\
+	fib_lookup/lookup.c fib_lookup/request.c \
+	\
+	route/pktloc_syntax.c route/pktloc_grammar.c route/pktloc.c
diff --git a/lib/addr.c b/lib/addr.c
index 68f7741..1f000e7 100644
--- a/lib/addr.c
+++ b/lib/addr.c
@@ -6,11 +6,11 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
- * @ingroup utils
+ * @ingroup core
  * @defgroup addr Abstract Address
  *
  * @par 1) Transform character string to abstract address
@@ -140,11 +140,11 @@
 	pos = dnet_num(src, &area);
 	if ((pos == 0) || (area > 63) ||
 	    ((*(src + pos) != '.') && (*(src + pos) != ',')))
-		return -EINVAL;
+		return -NLE_INVAL;
 
 	pos = dnet_num(src + pos + 1, &node);
 	if ((pos == 0) || (node > 1023))
-		return -EINVAL;
+		return -NLE_INVAL;
 
 	*(uint16_t *)addrbuf = dn_ntohs((area << 10) | node);
 
@@ -166,10 +166,8 @@
 	struct nl_addr *addr;
 	
 	addr = calloc(1, sizeof(*addr) + maxsize);
-	if (!addr) {
-		nl_errno(ENOMEM);
+	if (!addr)
 		return NULL;
-	}
 
 	addr->a_refcnt = 1;
 	addr->a_maxsize = maxsize;
@@ -203,9 +201,25 @@
 }
 
 /**
+ * Allocate abstract address based on netlink attribute.
+ * @arg nla		Netlink attribute of unspecific type.
+ * @arg family		Address family.
+ *
+ * Considers the netlink attribute payload a address of the specified
+ * family and allocates a new abstract address based on it.
+ *
+ * @return Newly allocated address handle or NULL.
+ */
+struct nl_addr *nl_addr_alloc_attr(struct nlattr *nla, int family)
+{
+	return nl_addr_build(family, nla_data(nla), nla_len(nla));
+}
+
+/**
  * Allocate abstract address object based on a character string
  * @arg addrstr		Address represented as character string.
  * @arg hint		Address family hint or AF_UNSPEC.
+ * @arg result		Pointer to store resulting address.
  *
  * Regognizes the following address formats:
  *@code
@@ -226,9 +240,9 @@
  * The prefix length may be appened at the end prefixed with a
  * slash, e.g. 10.0.0.0/8.
  *
- * @return Newly allocated abstract address object or NULL.
+ * @return 0 on success or a negative error code.
  */
-struct nl_addr *nl_addr_parse(const char *addrstr, int hint)
+int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result)
 {
 	int err, copy = 0, len = 0, family = AF_UNSPEC;
 	char *str, *prefix, buf[32];
@@ -236,7 +250,7 @@
 
 	str = strdup(addrstr);
 	if (!str) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 
@@ -274,8 +288,7 @@
 				goto prefix;
 
 			default:
-				err = nl_error(EINVAL, "Unsuported address" \
-				    "family for default address");
+				err = -NLE_AF_NOSUPPORT;
 				goto errout;
 		}
 	}
@@ -289,7 +302,7 @@
 			goto prefix;
 		}
 		if (hint == AF_INET) {
-			err = nl_error(EINVAL, "Invalid IPv4 address");
+			err = -NLE_NOADDR;
 			goto errout;
 		}
 	}
@@ -301,7 +314,7 @@
 			goto prefix;
 		}
 		if (hint == AF_INET6) {
-			err = nl_error(EINVAL, "Invalid IPv6 address");
+			err = -NLE_NOADDR;
 			goto errout;
 		}
 	}
@@ -323,7 +336,7 @@
 		}
 
 		if (hint == AF_LLC) {
-			err = nl_error(EINVAL, "Invalid link layer address");
+			err = -NLE_NOADDR;
 			goto errout;
 		}
 	}
@@ -336,7 +349,7 @@
 			goto prefix;
 		}
 		if (hint == AF_DECnet) {
-			err = nl_error(EINVAL, "Invalid DECnet address");
+			err = -NLE_NOADDR;
 			goto errout;
 		}
 	}
@@ -348,7 +361,7 @@
 			long l = strtol(s, &p, 16);
 
 			if (s == p || l > 0xff || i >= sizeof(buf)) {
-				err = -EINVAL;
+				err = -NLE_INVAL;
 				goto errout;
 			}
 
@@ -363,13 +376,13 @@
 		goto prefix;
 	}
 
-	err = nl_error(EINVAL, "Invalid address");
+	err = -NLE_NOADDR;
 	goto errout;
 
 prefix:
 	addr = nl_addr_alloc(len);
 	if (!addr) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 
@@ -383,18 +396,19 @@
 		long pl = strtol(++prefix, &p, 0);
 		if (p == prefix) {
 			nl_addr_destroy(addr);
-			err = -EINVAL;
+			err = -NLE_INVAL;
 			goto errout;
 		}
 		nl_addr_set_prefixlen(addr, pl);
 	} else
 		nl_addr_set_prefixlen(addr, len * 8);
 
+	*result = addr;
 	err = 0;
 errout:
 	free(str);
 
-	return err ? NULL : addr;
+	return err;
 }
 
 /**
@@ -619,7 +633,7 @@
 		struct sockaddr_in *sai = (struct sockaddr_in *) sa;
 
 		if (*salen < sizeof(*sai))
-			return -EINVAL;
+			return -NLE_INVAL;
 
 		sai->sin_family = addr->a_family;
 		memcpy(&sai->sin_addr, addr->a_addr, 4);
@@ -631,7 +645,7 @@
 		struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa;
 
 		if (*salen < sizeof(*sa6))
-			return -EINVAL;
+			return -NLE_INVAL;
 
 		sa6->sin6_family = addr->a_family;
 		memcpy(&sa6->sin6_addr, addr->a_addr, 16);
@@ -640,7 +654,7 @@
 		break;
 
 	default:
-		return -EINVAL;
+		return -NLE_INVAL;
 	}
 
 	return 0;
@@ -657,6 +671,7 @@
 /**
  * Call getaddrinfo() for an abstract address object.
  * @arg addr		Abstract address object.
+ * @arg result		Pointer to store resulting address list.
  * 
  * Calls getaddrinfo() for the specified abstract address in AI_NUMERICHOST
  * mode.
@@ -664,13 +679,11 @@
  * @note The caller is responsible for freeing the linked list using the
  *       interface provided by getaddrinfo(3).
  *
- * @return A linked list of addrinfo handles or  NULL with an error message
- *         associated.
+ * @return 0 on success or a negative error code.
  */
-struct addrinfo *nl_addr_info(struct nl_addr *addr)
+int nl_addr_info(struct nl_addr *addr, struct addrinfo **result)
 {
 	int err;
-	struct addrinfo *res;
 	char buf[INET6_ADDRSTRLEN+5];
 	struct addrinfo hint = {
 		.ai_flags = AI_NUMERICHOST,
@@ -679,13 +692,24 @@
 
 	nl_addr2str(addr, buf, sizeof(buf));
 
-	err = getaddrinfo(buf, NULL, &hint, &res);
+	err = getaddrinfo(buf, NULL, &hint, result);
 	if (err != 0) {
-		nl_error(err, gai_strerror(err));
-		return NULL;
+		switch (err) {
+		case EAI_ADDRFAMILY: return -NLE_AF_NOSUPPORT;
+		case EAI_AGAIN: return -NLE_AGAIN;
+		case EAI_BADFLAGS: return -NLE_INVAL;
+		case EAI_FAIL: return -NLE_NOADDR;
+		case EAI_FAMILY: return -NLE_AF_NOSUPPORT;
+		case EAI_MEMORY: return -NLE_NOMEM;
+		case EAI_NODATA: return -NLE_NOADDR;
+		case EAI_NONAME: return -NLE_OBJ_NOTFOUND;
+		case EAI_SERVICE: return -NLE_OPNOTSUPP;
+		case EAI_SOCKTYPE: return -NLE_BAD_SOCK;
+		default: return -NLE_FAILURE;
+		}
 	}
 
-	return res;
+	return 0;
 }
 
 /**
@@ -711,8 +735,12 @@
 	if (err < 0)
 		return err;
 
-	return getnameinfo((struct sockaddr *) &buf, salen,
-			   host, hostlen, NULL, 0, NI_NAMEREQD);
+	err = getnameinfo((struct sockaddr *) &buf, salen, host, hostlen,
+			  NULL, 0, NI_NAMEREQD);
+	if (err < 0)
+		return nl_syserr2nlerr(err);
+
+	return 0;
 }
 
 /** @} */
@@ -741,7 +769,7 @@
 int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len)
 {
 	if (len > addr->a_maxsize)
-		return -ERANGE;
+		return -NLE_RANGE;
 
 	addr->a_len = len;
 	memcpy(addr->a_addr, buf, len);
@@ -804,9 +832,12 @@
 	int i;
 	char tmp[16];
 
-	if (!addr->a_len) {
+	if (!addr || !addr->a_len) {
 		snprintf(buf, size, "none");
-		goto prefix;
+		if (addr)
+			goto prefix;
+		else
+			return buf;
 	}
 
 	switch (addr->a_family) {
diff --git a/lib/attr.c b/lib/attr.c
index 8568d32..298fbb1 100644
--- a/lib/attr.c
+++ b/lib/attr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #include <netlink-local.h>
@@ -21,89 +21,376 @@
  * @ingroup msg
  * @defgroup attr Attributes
  * Netlink Attributes Construction/Parsing Interface
- * @par 0) Introduction
- * Netlink attributes are chained together following each other:
+ *
+ * \section attr_sec Netlink Attributes
+ * Netlink attributes allow for data chunks of arbitary length to be
+ * attached to a netlink message. Each attribute is encoded with a
+ * type and length field, both 16 bits, stored in the attribute header
+ * preceding the attribute data. The main advantage of using attributes
+ * over packing everything into the family header is that the interface
+ * stays extendable as new attributes can supersede old attributes while
+ * remaining backwards compatible. Also attributes can be defined optional
+ * thus avoiding the transmission of unnecessary empty data blocks.
+ * Special nested attributes allow for more complex data structures to
+ * be transmitted, e.g. trees, lists, etc.
+ *
+ * While not required, netlink attributes typically follow the family
+ * header of a netlink message and must be properly aligned to NLA_ALIGNTO:
  * @code
- *    <------- nla_total_size(payload) ------->
- *    <---- nla_attr_size(payload) ----->
- *   +----------+- - -+- - - - - - - - - +- - -+-------- - -
- *   |  Header  | Pad |     Payload      | Pad |  Header
- *   +----------+- - -+- - - - - - - - - +- - -+-------- - -
- *                     <- nla_len(nla) ->      ^
- *   nla_data(nla)----^                        |
- *   nla_next(nla)-----------------------------'
+ *   +----------------+- - -+---------------+- - -+------------+- - -+
+ *   | Netlink Header | Pad | Family Header | Pad | Attributes | Pad |
+ *   +----------------+- - -+---------------+- - -+------------+- - -+
  * @endcode
  *
+ * The actual attributes are chained together each separately aligned to
+ * NLA_ALIGNTO. The position of an attribute is defined based on the
+ * length field of the preceding attributes:
+ * @code
+ *   +-------------+- - -+-------------+- - -+------
+ *   | Attribute 1 | Pad | Attribute 2 | Pad | ...
+ *   +-------------+- - -+-------------+- - -+------
+ *   nla_next(attr1)------^
+ * @endcode
+ *
+ * The attribute itself consists of the attribute header followed by
+ * the actual payload also aligned to NLA_ALIGNTO. The function nla_data()
+ * returns a pointer to the start of the payload while nla_len() returns
+ * the length of the payload in bytes.
+ *
+ * \b Note: Be aware, NLA_ALIGNTO equals to 4 bytes, therefore it is not
+ * safe to dereference any 64 bit data types directly.
+ *
+ * @code
+ *    <----------- nla_total_size(payload) ----------->
+ *    <-------- nla_attr_size(payload) --------->
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *   | Attribute Header | Pad |     Payload      | Pad |
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *   nla_data(nla)-------------^
+ *                             <- nla_len(nla) ->
+ * @endcode
+ *
+ * @subsection attr_datatypes Attribute Data Types
+ * A number of basic data types are supported to simplify access and
+ * validation of netlink attributes. This data type information is
+ * not encoded in the attribute, both the kernel and userspace part
+ * are required to share this information on their own.
+ *
+ * One of the major advantages of these basic types is the automatic
+ * validation of each attribute based on an attribute policy. The
+ * validation covers most of the checks required to safely use
+ * attributes and thus keeps the individual sanity check to a minimum.
+ *
+ * Never access attribute payload without ensuring basic validation
+ * first, attributes may:
+ * - not be present even though required
+ * - contain less actual payload than expected
+ * - fake a attribute length which exceeds the end of the message
+ * - contain unterminated character strings
+ *
+ * Policies are defined as array of the struct nla_policy. The array is
+ * indexed with the attribute type, therefore the array must be sized
+ * accordingly.
+ * @code
+ * static struct nla_policy my_policy[ATTR_MAX+1] = {
+ * 	[ATTR_FOO] = { .type = ..., .minlen = ..., .maxlen = ... },
+ * };
+ *
+ * err = nla_validate(attrs, attrlen, ATTR_MAX, &my_policy);
+ * @endcode
+ *
+ * Some basic validations are performed on every attribute, regardless of type.
+ * - If the attribute type exceeds the maximum attribute type specified or
+ *   the attribute type is lesser-or-equal than zero, the attribute will
+ *   be silently ignored.
+ * - If the payload length falls below the \a minlen value the attribute
+ *   will be rejected.
+ * - If \a maxlen is non-zero and the payload length exceeds the \a maxlen
+ *   value the attribute will be rejected.
+ *
+ *
+ * @par Unspecific Attribute (NLA_UNSPEC)
+ * This is the standard type if no type is specified. It is used for
+ * binary data of arbitary length. Typically this attribute carries
+ * a binary structure or a stream of bytes.
  * @par
- * The attribute header and payload must be aligned properly:
  * @code
- *  <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
- * +---------------------+- - -+- - - - - - - - - -+- - -+
- * |        Header       | Pad |     Payload       | Pad |
- * |   (struct nlattr)   | ing |                   | ing |
- * +---------------------+- - -+- - - - - - - - - -+- - -+
- *  <-------------- nlattr->nla_len -------------->
+ * // In this example, we will assume a binary structure requires to
+ * // be transmitted. The definition of the structure will typically
+ * // go into a header file available to both the kernel and userspace
+ * // side.
+ * //
+ * // Note: Be careful when putting 64 bit data types into a structure.
+ * // The attribute payload is only aligned to 4 bytes, dereferencing
+ * // the member may fail.
+ * struct my_struct {
+ *     int a;
+ *     int b;
+ * };
+ *
+ * // The validation function will not enforce an exact length match to
+ * // allow structures to grow as required. Note: While it is allowed
+ * // to add members to the end of the structure, changing the order or
+ * // inserting members in the middle of the structure will break your
+ * // binary interface.
+ * static struct nla_policy my_policy[ATTR_MAX+1] = {
+ *     [ATTR_MY_STRICT] = { .type = NLA_UNSPEC,
+ *                          .minlen = sizeof(struct my_struct) },
+ *
+ * // The binary structure is appened to the message using nla_put()
+ * struct my_struct foo = { .a = 1, .b = 2 };
+ * nla_put(msg, ATTR_MY_STRUCT, sizeof(foo), &foo);
+ *
+ * // On the receiving side, a pointer to the structure pointing inside
+ * // the message payload is returned by nla_get().
+ * if (attrs[ATTR_MY_STRUCT])
+ *     struct my_struct *foo = nla_get(attrs[ATTR_MY_STRUCT]);
  * @endcode
  *
- * @par Nested TLVs:
- * Nested TLVs are an array of TLVs nested into another TLV. This can be useful
- * to allow subsystems to have their own formatting rules without the need to
- * make the underlying layer be aware of it. It can also be useful to transfer
- * arrays, lists and flattened trees.
- * \code
- *  <-------------------- NLA_ALIGN(...) ------------------->
- * +---------------+- - - - - - - - - - - - - - - - - -+- - -+
- * |               |+---------+---------+- - -+-------+|     |
- * |  TLV Header   ||  TLV 1  |  TLV 2  |     | TLV n || Pad |
- * |               |+---------+---------+- - -+-------+|     |
- * +---------------+- - - - - - - - - - - - - - - - - -+- - -+
- *                  <--------- nla_data(nla) --------->
- * \endcode
- *
- * @par 1) Constructing a message with attributes
+ * @par Integers (NLA_U8, NLA_U16, NLA_U32, NLA_U64)
+ * Integers come in different sizes from 8 bit to 64 bit. However, since the
+ * payload length is aligned to 4 bytes, integers smaller than 32 bit are
+ * only useful to enforce the maximum range of values.
+ * @par
+ * \b Note: There is no difference made between signed and unsigned integers.
+ * The validation only enforces the minimal payload length required to store
+ * an integer of specified type.
+ * @par
  * @code
- * int param1 = 10;
- * char *param2 = "parameter text";
+ * // Even though possible, it does not make sense to specify .minlen or
+ * // .maxlen for integer types. The data types implies the corresponding
+ * // minimal payload length.
+ * static struct nla_policy my_policy[ATTR_MAX+1] = {
+ *     [ATTR_FOO] = { .type = NLA_U32 },
  *
- * struct nl_msg *msg = nlmsg_alloc();
- * nla_put_u32(msg, 1, param1);
- * nla_put_string(msg, 2, param2);
- * 
- * nl_send_auto_complete(handle, nl_msg_get(msg));
- * nlmsg_free(msg);
+ * // Numeric values can be appended directly using the respective
+ * // nla_put_uxxx() function
+ * nla_put_u32(msg, ATTR_FOO, 123);
+ *
+ * // Same for the receiving side.
+ * if (attrs[ATTR_FOO])
+ *     uint32_t foo = nla_get_u32(attrs[ATTR_FOO]);
  * @endcode
  *
- * @par 2) Constructing nested attributes
+ * @par Character string (NLA_STRING)
+ * This data type represents a NUL terminated character string of variable
+ * length. For binary data streams the type NLA_UNSPEC is recommended.
+ * @par
  * @code
- * struct nl_msg * nested_config(void)
- * {
- * 	int a = 5, int b = 10;
- * 	struct nl_msg *n = nlmsg_alloc();
- * 	nla_put_u32(n, 10, a);
- * 	nla_put_u32(n, 20, b);
- * 	return n;
+ * // Enforce a NUL terminated character string of at most 4 characters
+ * // including the NUL termination.
+ * static struct nla_policy my_policy[ATTR_MAX+1] = {
+ *     [ATTR_BAR] = { .type = NLA_STRING, maxlen = 4 },
+ *
+ * // nla_put_string() creates a string attribute of the necessary length
+ * // and appends it to the message including the NUL termination.
+ * nla_put_string(msg, ATTR_BAR, "some text");
+ *
+ * // It is safe to use the returned character string directly if the
+ * // attribute has been validated as the validation enforces the proper
+ * // termination of the string.
+ * if (attrs[ATTR_BAR])
+ *     char *text = nla_get_string(attrs[ATTR_BAR]);
+ * @endcode
+ *
+ * @par Flag (NLA_FLAG)
+ * This attribute type may be used to indicate the presence of a flag. The
+ * attribute is only valid if the payload length is zero. The presence of
+ * the attribute header indicates the presence of the flag.
+ * @par
+ * @code
+ * // This attribute type is special as .minlen and .maxlen have no effect.
+ * static struct nla_policy my_policy[ATTR_MAX+1] = {
+ *     [ATTR_FLAG] = { .type = NLA_FLAG },
+ *
+ * // nla_put_flag() appends a zero sized attribute to the message.
+ * nla_put_flag(msg, ATTR_FLAG);
+ *
+ * // There is no need for a receival function, the presence is the value.
+ * if (attrs[ATTR_FLAG])
+ *     // flag is present
+ * @endcode
+ *
+ * @par Micro Seconds (NLA_MSECS)
+ *
+ * @par Nested Attribute (NLA_NESTED)
+ * Attributes can be nested and put into a container to create groups, lists
+ * or to construct trees of attributes. Nested attributes are often used to
+ * pass attributes to a subsystem where the top layer has no knowledge of the
+ * configuration possibilities of each subsystem.
+ * @par
+ * \b Note: When validating the attributes using nlmsg_validate() or
+ * nlmsg_parse() it will only affect the top level attributes. Each
+ * level of nested attributes must be validated seperately using
+ * nla_parse_nested() or nla_validate().
+ * @par
+ * @code
+ * // The minimal length policy may be used to enforce the presence of at
+ * // least one attribute.
+ * static struct nla_policy my_policy[ATTR_MAX+1] = {
+ *     [ATTR_OPTS] = { .type = NLA_NESTED, minlen = NLA_HDRLEN },
+ *
+ * // Nested attributes are constructed by enclosing the attributes
+ * // to be nested with calls to nla_nest_start() respetively nla_nest_end().
+ * struct nlattr *opts = nla_nest_start(msg, ATTR_OPTS);
+ * nla_put_u32(msg, ATTR_FOO, 123);
+ * nla_put_string(msg, ATTR_BAR, "some text");
+ * nla_nest_end(msg, opts);
+ *
+ * // Various methods exist to parse nested attributes, the easiest being
+ * // nla_parse_nested() which also allows validation in the same step.
+ * if (attrs[ATTR_OPTS]) {
+ *     struct nlattr *nested[ATTR_MAX+1];
+ *
+ *     nla_parse_nested(nested, ATTR_MAX, attrs[ATTR_OPTS], &policy);
+ *
+ *     if (nested[ATTR_FOO])
+ *         uint32_t foo = nla_get_u32(nested[ATTR_FOO]);
  * }
- *
- * ...
- * struct nl_msg *m = nlmsg_alloc();
- * struct nl_msg *nest = nested_config();
- * nla_put_nested(m, 1, nest);
- *
- * nl_send_auto_complete(handle, nl_msg_get(m));
- * nlmsg_free(nest);
- * nlmsg_free(m);
  * @endcode
+ *
+ * @subsection attr_exceptions Exception Based Attribute Construction
+ * Often a large number of attributes are added to a message in a single
+ * function. In order to simplify error handling, a second set of
+ * construction functions exist which jump to a error label when they
+ * fail instead of returning an error code. This second set consists
+ * of macros which are named after their error code based counterpart
+ * except that the name is written all uppercase.
+ *
+ * All of the macros jump to the target \c nla_put_failure if they fail.
+ * @code
+ * void my_func(struct nl_msg *msg)
+ * {
+ *     NLA_PUT_U32(msg, ATTR_FOO, 10);
+ *     NLA_PUT_STRING(msg, ATTR_BAR, "bar");
+ *
+ *     return 0;
+ *
+ * nla_put_failure:
+ *     return -NLE_NOMEM;
+ * }
+ * @endcode
+ *
+ * @subsection attr_examples Examples
+ * @par Example 1.1 Constructing a netlink message with attributes.
+ * @code
+ * struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu)
+ * {
+ *     struct nl_msg *msg;
+ *     struct nlattr *info, *vlan;
+ *     struct ifinfomsg ifi = {
+ *         .ifi_family = AF_INET,
+ *         .ifi_index = ifindex,
+ *     };
+ *
+ *     // Allocate a new netlink message, type=RTM_SETLINK, flags=NLM_F_ECHO
+ *     if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_ECHO)))
+ *         return NULL;
+ *
+ *     // Append the family specific header (struct ifinfomsg)
+ *     if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
+ *         goto nla_put_failure
+ *
+ *     // Append a 32 bit integer attribute to carry the MTU
+ *     NLA_PUT_U32(msg, IFLA_MTU, mtu);
+ *
+ *     // Append a unspecific attribute to carry the link layer address
+ *     NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr);
+ *
+ *     // Append a container for nested attributes to carry link information
+ *     if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
+ *         goto nla_put_failure;
+ *
+ *     // Put a string attribute into the container
+ *     NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan");
+ *
+ *     // Append another container inside the open container to carry
+ *     // vlan specific attributes
+ *     if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA)))
+ *         goto nla_put_failure;
+ *
+ *     // add vlan specific info attributes here...
+ *
+ *     // Finish nesting the vlan attributes and close the second container.
+ *     nla_nest_end(msg, vlan);
+ *
+ *     // Finish nesting the link info attribute and close the first container.
+ *     nla_nest_end(msg, info);
+ *
+ *     return msg;
+ *
+ * // If any of the construction macros fails, we end up here.
+ * nla_put_failure:
+ *     nlmsg_free(msg);
+ *     return NULL;
+ * }
+ * @endcode
+ *
+ * @par Example 2.1 Parsing a netlink message with attributes.
+ * @code
+ * int parse_message(struct nl_msg *msg)
+ * {
+ *     // The policy defines two attributes: a 32 bit integer and a container
+ *     // for nested attributes.
+ *     struct nla_policy attr_policy[ATTR_MAX+1] = {
+ *         [ATTR_FOO] = { .type = NLA_U32 },
+ *         [ATTR_BAR] = { .type = NLA_NESTED },
+ *     };
+ *     struct nlattr *attrs[ATTR_MAX+1];
+ *     int err;
+ *
+ *     // The nlmsg_parse() function will make sure that the message contains
+ *     // enough payload to hold the header (struct my_hdr), validates any
+ *     // attributes attached to the messages and stores a pointer to each
+ *     // attribute in the attrs[] array accessable by attribute type.
+ *     if ((err = nlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs,
+ *                            ATTR_MAX, attr_policy)) < 0)
+ *         goto errout;
+ *
+ *     if (attrs[ATTR_FOO]) {
+ *         // It is safe to directly access the attribute payload without
+ *         // any further checks since nlmsg_parse() enforced the policy.
+ *         uint32_t foo = nla_get_u32(attrs[ATTR_FOO]);
+ *     }
+ *
+ *     if (attrs[ATTR_BAR]) {
+ *         struct nlattr *nested[NESTED_MAX+1];
+ *
+ *         // Attributes nested in a container can be parsed the same way
+ *         // as top level attributes.
+ *         if ((err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR],
+ *                                     nested_policy)) < 0)
+ *             goto errout;
+ *
+ *         // Process nested attributes here.
+ *     }
+ *
+ *     err = 0;
+ * errout:
+ *     return err;
+ * }
+ * @endcode
+ *
  * @{
  */
 
 /**
- * @name Size Calculations
+ * @name Attribute Size Calculation
  * @{
  */
 
 /**
- * length of attribute not including padding
- * @arg payload		length of payload
+ * Return size of attribute whithout padding.
+ * @arg payload		Payload length of attribute.
+ *
+ * @code
+ *    <-------- nla_attr_size(payload) --------->
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *   | Attribute Header | Pad |     Payload      | Pad |
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ * @endcode
+ *
+ * @return Size of attribute in bytes without padding.
  */
 int nla_attr_size(int payload)
 {
@@ -111,8 +398,17 @@
 }
 
 /**
- * total length of attribute including padding
- * @arg payload		length of payload
+ * Return size of attribute including padding.
+ * @arg payload		Payload length of attribute.
+ *
+ * @code
+ *    <----------- nla_total_size(payload) ----------->
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *   | Attribute Header | Pad |     Payload      | Pad |
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ * @endcode
+ *
+ * @return Size of attribute in bytes.
  */
 int nla_total_size(int payload)
 {
@@ -120,8 +416,17 @@
 }
 
 /**
- * length of padding at the tail of the attribute
- * @arg payload		length of payload
+ * Return length of padding at the tail of the attribute.
+ * @arg payload		Payload length of attribute.
+ *
+ * @code
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *   | Attribute Header | Pad |     Payload      | Pad |
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *                                                <--->  
+ * @endcode
+ *
+ * @return Length of padding in bytes.
  */
 int nla_padlen(int payload)
 {
@@ -131,13 +436,15 @@
 /** @} */
 
 /**
- * @name Payload Access
+ * @name Parsing Attributes
  * @{
  */
 
 /**
- * attribute type
- * @arg nla		netlink attribute
+ * Return type of the attribute.
+ * @arg nla		Attribute.
+ *
+ * @return Type of attribute.
  */
 int nla_type(const struct nlattr *nla)
 {
@@ -145,8 +452,10 @@
 }
 
 /**
- * head of payload
- * @arg nla		netlink attribute
+ * Return pointer to the payload section.
+ * @arg nla		Attribute.
+ *
+ * @return Pointer to start of payload section.
  */
 void *nla_data(const struct nlattr *nla)
 {
@@ -154,25 +463,27 @@
 }
 
 /**
- * length of payload
- * @arg nla		netlink attribute
+ * Return length of the payload .
+ * @arg nla		Attribute
+ *
+ * @return Length of payload in bytes.
  */
 int nla_len(const struct nlattr *nla)
 {
 	return nla->nla_len - NLA_HDRLEN;
 }
 
-/** @} */
-
 /**
- * @name Attribute Parsing
- * @{
- */
-
-/**
- * check if the netlink attribute fits into the remaining bytes
- * @arg nla		netlink attribute
- * @arg remaining	number of bytes remaining in attribute stream
+ * Check if the attribute header and payload can be accessed safely.
+ * @arg nla		Attribute of any kind.
+ * @arg remaining	Number of bytes remaining in attribute stream.
+ *
+ * Verifies that the header and payload do not exceed the number of
+ * bytes left in the attribute stream. This function must be called
+ * before access the attribute header or payload when iterating over
+ * the attribute stream using nla_next().
+ *
+ * @return True if the attribute can be accessed safely, false otherwise.
  */
 int nla_ok(const struct nlattr *nla, int remaining)
 {
@@ -182,12 +493,20 @@
 }
 
 /**
- * next netlink attribte in attribute stream
- * @arg nla		netlink attribute
- * @arg remaining	number of bytes remaining in attribute stream
+ * Return next attribute in a stream of attributes.
+ * @arg nla		Attribute of any kind.
+ * @arg remaining	Variable to count remaining bytes in stream.
  *
- * @return the next netlink attribute in the attribute stream and
- * decrements remaining by the size of the current attribute.
+ * Calculates the offset to the next attribute based on the attribute
+ * given. The attribute provided is assumed to be accessible, the
+ * caller is responsible to use nla_ok() beforehand. The offset (length
+ * of specified attribute including padding) is then subtracted from
+ * the remaining bytes variable and a pointer to the next attribute is
+ * returned.
+ *
+ * nla_next() can be called as long as remainig is >0.
+ *
+ * @return Pointer to next attribute.
  */
 struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
 {
@@ -203,7 +522,6 @@
 	[NLA_U32]	= sizeof(uint32_t),
 	[NLA_U64]	= sizeof(uint64_t),
 	[NLA_STRING]	= 1,
-	[NLA_NESTED]	= NLA_HDRLEN,
 };
 
 static int validate_nla(struct nlattr *nla, int maxtype,
@@ -226,18 +544,18 @@
 		minlen = nla_attr_minlen[pt->type];
 
 	if (pt->type == NLA_FLAG && nla_len(nla) > 0)
-		return nl_errno(ERANGE);
+		return -NLE_RANGE;
 
 	if (nla_len(nla) < minlen)
-		return nl_errno(ERANGE);
+		return -NLE_RANGE;
 
 	if (pt->maxlen && nla_len(nla) > pt->maxlen)
-		return nl_errno(ERANGE);
+		return -NLE_RANGE;
 
 	if (pt->type == NLA_STRING) {
 		char *data = nla_data(nla);
 		if (data[nla_len(nla) - 1] != '\0')
-			return nl_errno(EINVAL);
+			return -NLE_INVAL;
 	}
 
 	return 0;
@@ -245,18 +563,21 @@
 
 
 /**
- * Parse a stream of attributes into a tb buffer
- * @arg tb		destination array with maxtype+1 elements
- * @arg maxtype		maximum attribute type to be expected
- * @arg head		head of attribute stream
- * @arg len		length of attribute stream
- * @arg policy		validation policy
+ * Create attribute index based on a stream of attributes.
+ * @arg tb		Index array to be filled (maxtype+1 elements).
+ * @arg maxtype		Maximum attribute type expected and accepted.
+ * @arg head		Head of attribute stream.
+ * @arg len		Length of attribute stream.
+ * @arg policy		Attribute validation policy.
  *
- * Parses a stream of attributes and stores a pointer to each attribute in
- * the tb array accessable via the attribute type. Attributes with a type
- * exceeding maxtype will be silently ignored for backwards compatibility
- * reasons. policy may be set to NULL if no validation is required.
+ * Iterates over the stream of attributes and stores a pointer to each
+ * attribute in the index array using the attribute type as index to
+ * the array. Attribute with a type greater than the maximum type
+ * specified will be silently ignored in order to maintain backwards
+ * compatibility. If \a policy is not NULL, the attribute will be
+ * validated using the specified policy.
  *
+ * @see nla_validate
  * @return 0 on success or a negative error code.
  */
 int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
@@ -295,33 +616,20 @@
 	return err;
 }
 
-
 /**
- * parse nested attributes
- * @arg tb		destination array with maxtype+1 elements
- * @arg maxtype		maximum attribute type to be expected
- * @arg nla		attribute containing the nested attributes
- * @arg policy		validation policy
+ * Validate a stream of attributes.
+ * @arg head		Head of attributes stream.
+ * @arg len		Length of attributes stream.
+ * @arg maxtype		Maximum attribute type expected and accepted.
+ * @arg policy		Validation policy.
  *
- * @see nla_parse()
- */
-int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
-		     struct nla_policy *policy)
-{
-	return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
-}
-
-/**
- * Validate a stream of attributes
- * @arg head		head of attribute stream
- * @arg len		length of attribute stream
- * @arg maxtype		maximum attribute type to be expected
- * @arg policy		validation policy
+ * Iterates over the stream of attributes and validates each attribute
+ * one by one using the specified policy. Attributes with a type greater
+ * than the maximum type specified will be silently ignored in order to
+ * maintain backwards compatibility.
  *
- * Validates all attributes in the specified attribute stream
- * against the specified policy. Attributes with a type exceeding
- * maxtype will be ignored. See documenation of struct nla_policy
- * for more details.
+ * See \ref attr_datatypes for more details on what kind of validation
+ * checks are performed on each attribute data type.
  *
  * @return 0 on success or a negative error code.
  */
@@ -343,12 +651,16 @@
 }
 
 /**
- * Find a specific attribute in a stream of attributes
- * @arg head		head of attribute stream
- * @arg len		length of attribute stream
- * @arg attrtype	type of attribute to look for
+ * Find a single attribute in a stream of attributes.
+ * @arg head		Head of attributes stream.
+ * @arg len		Length of attributes stream.
+ * @arg attrtype	Attribute type to look for.
  *
- * @return the first attribute in the stream matching the specified type.
+ * Iterates over the stream of attributes and compares each type with
+ * the type specified. Returns the first attribute which matches the
+ * type.
+ *
+ * @return Pointer to attribute found or NULL.
  */
 struct nlattr *nla_find(struct nlattr *head, int len, int attrtype)
 {
@@ -365,20 +677,20 @@
 /** @} */
 
 /**
- * @name Utilities
+ * @name Helper Functions
  * @{
  */
 
 /**
- * Copy a netlink attribute into another memory area
- * @arg dest		where to copy to memcpy
- * @arg src		netlink attribute to copy from
- * @arg count		size of the destination area
+ * Copy attribute payload to another memory area.
+ * @arg dest		Pointer to destination memory area.
+ * @arg src		Attribute
+ * @arg count		Number of bytes to copy at most.
  *
  * Note: The number of bytes copied is limited by the length of
- *       attribute's payload. memcpy
+ *       the attribute payload.
  *
- * @return the number of bytes copied.
+ * @return The number of bytes copied to dest.
  */
 int nla_memcpy(void *dest, struct nlattr *src, int count)
 {
@@ -394,16 +706,16 @@
 }
 
 /**
- * Copy string attribute payload into a sized buffer
- * @arg dst		where to copy the string to
- * @arg nla		attribute to copy the string from
- * @arg dstsize		size of destination buffer
+ * Copy string attribute payload to a buffer.
+ * @arg dst		Pointer to destination buffer.
+ * @arg nla		Attribute of type NLA_STRING.
+ * @arg dstsize		Size of destination buffer in bytes.
  *
- * Copies at most dstsize - 1 bytes into the destination buffer.
- * The result is always a valid NUL-terminated string. Unlike
+ * Copies at most dstsize - 1 bytes to the destination buffer.
+ * The result is always a valid NUL terminated string. Unlike
  * strlcpy the destination buffer is always padded out.
  *
- * @return the length of the source buffer.
+ * @return The length of string attribute without the terminating NUL.
  */
 size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
 {
@@ -424,13 +736,15 @@
 }
 
 /**
- * Compare an attribute with sized memory area
- * @arg nla		netlink attribute
- * @arg data		memory area
- * @arg size		size of memory area
+ * Compare attribute payload with memory area.
+ * @arg nla		Attribute.
+ * @arg data		Memory area to compare to.
+ * @arg size		Number of bytes to compare.
+ *
+ * @see memcmp(3)
+ * @return An integer less than, equal to, or greater than zero.
  */
-int nla_memcmp(const struct nlattr *nla, const void *data,
-			     size_t size)
+int nla_memcmp(const struct nlattr *nla, const void *data, size_t size)
 {
 	int d = nla_len(nla) - size;
 
@@ -441,9 +755,12 @@
 }
 
 /**
- * Compare a string attribute against a string
- * @arg nla		netlink string attribute
- * @arg str		another string
+ * Compare string attribute payload with string
+ * @arg nla		Attribute of type NLA_STRING.
+ * @arg str		NUL terminated string.
+ *
+ * @see strcmp(3)
+ * @return An integer less than, equal to, or greater than zero.
  */
 int nla_strcmp(const struct nlattr *nla, const char *str)
 {
@@ -459,152 +776,295 @@
 /** @} */
 
 /**
- * @name Attribute Construction
+ * @name Unspecific Attribute
  * @{
  */
 
 /**
- * reserve room for attribute on the skb
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg attrlen		length of attribute payload
+ * Reserve space for a attribute.
+ * @arg msg		Netlink Message.
+ * @arg attrtype	Attribute Type.
+ * @arg attrlen		Length of payload.
  *
- * Adds a netlink attribute header to a netlink message and reserves
- * room for the payload but does not copy it.
+ * Reserves room for a attribute in the specified netlink message and
+ * fills in the attribute header (type, length). Returns NULL if there
+ * is unsuficient space for the attribute.
+ *
+ * Any padding between payload and the start of the next attribute is
+ * zeroed out.
+ *
+ * @return Pointer to start of attribute or NULL on failure.
  */
-struct nlattr *nla_reserve(struct nl_msg *n, int attrtype, int attrlen)
+struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen)
 {
 	struct nlattr *nla;
 	int tlen;
 	
-	tlen = NLMSG_ALIGN(n->nm_nlh->nlmsg_len) + nla_total_size(attrlen);
+	tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen);
 
-	if ((tlen + n->nm_nlh->nlmsg_len) > n->nm_size) {
-		nl_errno(ENOBUFS);
+	if ((tlen + msg->nm_nlh->nlmsg_len) > msg->nm_size)
 		return NULL;
-	}
 
-	nla = (struct nlattr *) nlmsg_tail(n->nm_nlh);
+	nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
 	nla->nla_type = attrtype;
 	nla->nla_len = nla_attr_size(attrlen);
 
 	memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen));
-	n->nm_nlh->nlmsg_len = tlen;
+	msg->nm_nlh->nlmsg_len = tlen;
 
 	NL_DBG(2, "msg %p: Reserved %d bytes at offset +%td for attr %d "
-		  "nlmsg_len=%d\n", n, attrlen,
-		  (void *) nla - nlmsg_data(n->nm_nlh),
-		  attrtype, n->nm_nlh->nlmsg_len);
+		  "nlmsg_len=%d\n", msg, attrlen,
+		  (void *) nla - nlmsg_data(msg->nm_nlh),
+		  attrtype, msg->nm_nlh->nlmsg_len);
 
 	return nla;
 }
 
 /**
- * Add a netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg attrlen		length of attribute payload
- * @arg data		head of attribute payload
+ * Add a unspecific attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg datalen		Length of data to be used as payload.
+ * @arg data		Pointer to data to be used as attribute payload.
  *
- * @return -1 if the tailroom of the skb is insufficient to store
- * the attribute header and payload.
+ * Reserves room for a unspecific attribute and copies the provided data
+ * into the message as payload of the attribute. Returns an error if there
+ * is insufficient space for the attribute.
+ *
+ * @see nla_reserve
+ * @return 0 on success or a negative error code.
  */
-int nla_put(struct nl_msg *n, int attrtype, int attrlen, const void *data)
+int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data)
 {
 	struct nlattr *nla;
 
-	nla = nla_reserve(n, attrtype, attrlen);
+	nla = nla_reserve(msg, attrtype, datalen);
 	if (!nla)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
-	memcpy(nla_data(nla), data, attrlen);
+	memcpy(nla_data(nla), data, datalen);
 	NL_DBG(2, "msg %p: Wrote %d bytes at offset +%td for attr %d\n",
-	       n, attrlen, (void *) nla - nlmsg_data(n->nm_nlh), attrtype);
+	       msg, datalen, (void *) nla - nlmsg_data(msg->nm_nlh), attrtype);
 
 	return 0;
 }
 
 /**
- * Add a nested netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg nested		netlink attribute to nest
+ * Add abstract data as unspecific attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg data		Abstract data object.
  *
- * @return -1 if the tailroom of the skb is insufficient to store
- * the attribute header and payload.
+ * Equivalent to nla_put() except that the length of the payload is
+ * derived from the abstract data object.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
  */
-int nla_put_nested(struct nl_msg *n, int attrtype, struct nl_msg *nested)
+int nla_put_data(struct nl_msg *msg, int attrtype, struct nl_data *data)
 {
-	return nla_put(n, attrtype, nlmsg_len(nested->nm_nlh),
-		       nlmsg_data(nested->nm_nlh));
+	return nla_put(msg, attrtype, nl_data_get_size(data),
+		       nl_data_get(data));
 }
 
 /**
- * Add a u16 netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg value		numeric value
+ * Add abstract address as unspecific attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg addr		Abstract address object.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
  */
-int nla_put_u8(struct nl_msg *n, int attrtype, uint8_t value)
+int nla_put_addr(struct nl_msg *msg, int attrtype, struct nl_addr *addr)
 {
-	return nla_put(n, attrtype, sizeof(uint8_t), &value);
+	return nla_put(msg, attrtype, nl_addr_get_len(addr),
+		       nl_addr_get_binary_addr(addr));
+}
+
+/** @} */
+
+/**
+ * @name Integer Attributes
+ */
+
+/**
+ * Add 8 bit integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value)
+{
+	return nla_put(msg, attrtype, sizeof(uint8_t), &value);
 }
 
 /**
- * Add a u16 netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg value		numeric value
+ * Return value of 8 bit integer attribute.
+ * @arg nla		8 bit integer attribute
+ *
+ * @return Payload as 8 bit integer.
  */
-int nla_put_u16(struct nl_msg *n, int attrtype, uint16_t value)
+uint8_t nla_get_u8(struct nlattr *nla)
 {
-	return nla_put(n, attrtype, sizeof(uint16_t), &value);
+	return *(uint8_t *) nla_data(nla);
 }
 
 /**
- * Add a u32 netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg value		numeric value
+ * Add 16 bit integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
  */
-int nla_put_u32(struct nl_msg *n, int attrtype, uint32_t value)
+int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value)
 {
-	return nla_put(n, attrtype, sizeof(uint32_t), &value);
+	return nla_put(msg, attrtype, sizeof(uint16_t), &value);
 }
 
 /**
- * Add a u64 netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg value		numeric value
+ * Return payload of 16 bit integer attribute.
+ * @arg nla		16 bit integer attribute
+ *
+ * @return Payload as 16 bit integer.
  */
-int nla_put_u64(struct nl_msg *n, int attrtype, uint64_t value)
+uint16_t nla_get_u16(struct nlattr *nla)
 {
-	return nla_put(n, attrtype, sizeof(uint64_t), &value);
+	return *(uint16_t *) nla_data(nla);
 }
 
 /**
- * Add a string netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg str		NUL terminated string
+ * Add 32 bit integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
  */
-int nla_put_string(struct nl_msg *n, int attrtype, const char *str)
+int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value)
 {
-	return nla_put(n, attrtype, strlen(str) + 1, str);
+	return nla_put(msg, attrtype, sizeof(uint32_t), &value);
 }
 
 /**
- * Add a flag netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
+ * Return payload of 32 bit integer attribute.
+ * @arg nla		32 bit integer attribute.
+ *
+ * @return Payload as 32 bit integer.
  */
-int nla_put_flag(struct nl_msg *n, int attrtype)
+uint32_t nla_get_u32(struct nlattr *nla)
 {
-	return nla_put(n, attrtype, 0, NULL);
+	return *(uint32_t *) nla_data(nla);
 }
 
 /**
+ * Add 64 bit integer attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg value		Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value)
+{
+	return nla_put(msg, attrtype, sizeof(uint64_t), &value);
+}
+
+/**
+ * Return payload of u64 attribute
+ * @arg nla		u64 netlink attribute
+ *
+ * @return Payload as 64 bit integer.
+ */
+uint64_t nla_get_u64(struct nlattr *nla)
+{
+	uint64_t tmp;
+
+	nla_memcpy(&tmp, nla, sizeof(tmp));
+
+	return tmp;
+}
+
+/** @} */
+
+/**
+ * @name String Attribute
+ */
+
+/**
+ * Add string attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg str		NUL terminated string.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+int nla_put_string(struct nl_msg *msg, int attrtype, const char *str)
+{
+	return nla_put(msg, attrtype, strlen(str) + 1, str);
+}
+
+/**
+ * Return payload of string attribute.
+ * @arg nla		String attribute.
+ *
+ * @return Pointer to attribute payload.
+ */
+char *nla_get_string(struct nlattr *nla)
+{
+	return (char *) nla_data(nla);
+}
+
+char *nla_strdup(struct nlattr *nla)
+{
+	return strdup(nla_get_string(nla));
+}
+
+/** @} */
+
+/**
+ * @name Flag Attribute
+ */
+
+/**
+ * Add flag netlink attribute to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+int nla_put_flag(struct nl_msg *msg, int attrtype)
+{
+	return nla_put(msg, attrtype, 0, NULL);
+}
+
+/**
+ * Return true if flag attribute is set.
+ * @arg nla		Flag netlink attribute.
+ *
+ * @return True if flag is set, otherwise false.
+ */
+int nla_get_flag(struct nlattr *nla)
+{
+	return !!nla;
+}
+
+/** @} */
+
+/**
+ * @name Microseconds Attribute
+ */
+
+/**
  * Add a msecs netlink attribute to a netlink message
  * @arg n		netlink message
  * @arg attrtype	attribute type
@@ -616,136 +1076,6 @@
 }
 
 /**
- * Add an abstract data netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg data		abstract data
- */
-int nla_put_data(struct nl_msg *n, int attrtype, struct nl_data *data)
-{
-	return nla_put(n, attrtype, nl_data_get_size(data),
-		       nl_data_get(data));
-}
-
-/**
- * Add an abstract address netlink attribute to a netlink message
- * @arg n		netlink message
- * @arg attrtype	attribute type
- * @arg addr		abstract address
- */
-int nla_put_addr(struct nl_msg *n, int attrtype, struct nl_addr *addr)
-{
-	return nla_put(n, attrtype, nl_addr_get_len(addr),
-		       nl_addr_get_binary_addr(addr));
-}
-
-/** @} */
-
-/**
- * @name Attribute Nesting
- * @{
- */
-
-/**
- * Start a new level of nested attributes
- * @arg n		netlink message
- * @arg attrtype	attribute type of container
- *
- * @return the container attribute
- */
-struct nlattr *nla_nest_start(struct nl_msg *n, int attrtype)
-{
-	struct nlattr *start = (struct nlattr *) nlmsg_tail(n->nm_nlh);
-
-	if (nla_put(n, attrtype, 0, NULL) < 0)
-		return NULL;
-
-	return start;
-}
-
-/**
- * Finalize nesting of attributes
- * @arg n		netlink message
- * @arg start		container attribute
- *
- * Corrects the container attribute header to include the all
- * appeneded attributes.
- *
- * @return the total data length of the skb.
- */
-int nla_nest_end(struct nl_msg *n, struct nlattr *start)
-{
-	start->nla_len = (unsigned char *) nlmsg_tail(n->nm_nlh) -
-				(unsigned char *) start;
-	return 0;
-}
-
-/** @} */
-
-/**
- * @name Attribute Reading
- * @{
- */
-
-/**
- * Return payload of u32 attribute
- * @arg nla		u32 netlink attribute
- */
-uint32_t nla_get_u32(struct nlattr *nla)
-{
-	return *(uint32_t *) nla_data(nla);
-}
-
-/**
- * Return payload of u16 attribute
- * @arg nla		u16 netlink attribute
- */
-uint16_t nla_get_u16(struct nlattr *nla)
-{
-	return *(uint16_t *) nla_data(nla);
-}
-
-/**
- * Return payload of u8 attribute
- * @arg nla		u8 netlink attribute
- */
-uint8_t nla_get_u8(struct nlattr *nla)
-{
-	return *(uint8_t *) nla_data(nla);
-}
-
-/**
- * Return payload of u64 attribute
- * @arg nla		u64 netlink attribute
- */
-uint64_t nla_get_u64(struct nlattr *nla)
-{
-	uint64_t tmp;
-
-	nla_memcpy(&tmp, nla, sizeof(tmp));
-
-	return tmp;
-}
-
-/**
- * return payload of string attribute
- * @arg nla		string netlink attribute
- */
-char *nla_get_string(struct nlattr *nla)
-{
-	return (char *) nla_data(nla);
-}
-
-/**
- * Return payload of flag attribute
- * @arg nla		flag netlink attribute
- */
-int nla_get_flag(struct nlattr *nla)
-{
-	return !!nla;
-}
-
-/**
  * Return payload of msecs attribute
  * @arg nla		msecs netlink attribute
  *
@@ -756,27 +1086,82 @@
 	return nla_get_u64(nla);
 }
 
+/** @} */
+
 /**
- * Return payload of address attribute
- * @arg nla		address netlink attribute
- * @arg family		address family
- *
- * @return Newly allocated address handle or NULL
+ * @name Nested Attribute
  */
-struct nl_addr *nla_get_addr(struct nlattr *nla, int family)
+
+/**
+ * Add nested attributes to netlink message.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type.
+ * @arg nested		Message containing attributes to be nested.
+ *
+ * Takes the attributes found in the \a nested message and appends them
+ * to the message \a msg nested in a container of the type \a attrtype.
+ * The \a nested message may not have a family specific header.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested)
 {
-	return nl_addr_build(family, nla_data(nla), nla_len(nla));
+	return nla_put(msg, attrtype, nlmsg_len(nested->nm_nlh),
+		       nlmsg_data(nested->nm_nlh));
+}
+
+
+/**
+ * Start a new level of nested attributes.
+ * @arg msg		Netlink message.
+ * @arg attrtype	Attribute type of container.
+ *
+ * @return Pointer to container attribute.
+ */
+struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype)
+{
+	struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
+
+	if (nla_put(msg, attrtype, 0, NULL) < 0)
+		return NULL;
+
+	return start;
 }
 
 /**
- * Return payload of abstract data attribute
- * @arg nla		abstract data netlink attribute
+ * Finalize nesting of attributes.
+ * @arg msg		Netlink message.
+ * @arg start		Container attribute as returned from nla_nest_start().
  *
- * @return Newly allocated abstract data handle or NULL
+ * Corrects the container attribute header to include the appeneded attributes.
+ *
+ * @return 0
  */
-struct nl_data *nla_get_data(struct nlattr *nla)
+int nla_nest_end(struct nl_msg *msg, struct nlattr *start)
 {
-	return nl_data_alloc(nla_data(nla), nla_len(nla));
+	start->nla_len = (unsigned char *) nlmsg_tail(msg->nm_nlh) -
+				(unsigned char *) start;
+	return 0;
+}
+
+/**
+ * Create attribute index based on nested attribute
+ * @arg tb		Index array to be filled (maxtype+1 elements).
+ * @arg maxtype		Maximum attribute type expected and accepted.
+ * @arg nla		Nested Attribute.
+ * @arg policy		Attribute validation policy.
+ *
+ * Feeds the stream of attributes nested into the specified attribute
+ * to nla_parse().
+ *
+ * @see nla_parse
+ * @return 0 on success or a negative error code.
+ */
+int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
+		     struct nla_policy *policy)
+{
+	return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
 }
 
 /** @} */
diff --git a/lib/cache.c b/lib/cache.c
index 285bc31..2b24946 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -175,10 +175,8 @@
 	struct nl_cache *cache;
 
 	cache = calloc(1, sizeof(*cache));
-	if (!cache) {
-		nl_errno(ENOMEM);
+	if (!cache)
 		return NULL;
-	}
 
 	nl_init_list_head(&cache->c_items);
 	cache->c_ops = ops;
@@ -188,22 +186,43 @@
 	return cache;
 }
 
+int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock,
+			    struct nl_cache **result)
+{
+	struct nl_cache *cache;
+	int err;
+	
+	if (!(cache = nl_cache_alloc(ops)))
+		return -NLE_NOMEM;
+
+	if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
+		nl_cache_free(cache);
+		return err;
+	}
+
+	*result = cache;
+	return 0;
+}
+
 /**
  * Allocate an empty cache based on type name
  * @arg kind		Name of cache type
  * @return A newly allocated and initialized cache.
  */
-struct nl_cache *nl_cache_alloc_name(const char *kind)
+int nl_cache_alloc_name(const char *kind, struct nl_cache **result)
 {
 	struct nl_cache_ops *ops;
+	struct nl_cache *cache;
 
 	ops = nl_cache_ops_lookup(kind);
-	if (!ops) {
-		nl_error(ENOENT, "Unable to lookup cache \"%s\"", kind);
-		return NULL;
-	}
+	if (!ops)
+		return -NLE_NOCACHE;
 
-	return nl_cache_alloc(ops);
+	if (!(cache = nl_cache_alloc(ops)))
+		return -NLE_NOMEM;
+
+	*result = cache;
+	return 0;
 }
 
 /**
@@ -264,6 +283,9 @@
  */
 void nl_cache_free(struct nl_cache *cache)
 {
+	if (!cache)
+		return;
+
 	nl_cache_clear(cache);
 	NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
 	free(cache);
@@ -304,12 +326,12 @@
 	struct nl_object *new;
 
 	if (cache->c_ops->co_obj_ops != obj->ce_ops)
-		return nl_error(EINVAL, "Object mismatches cache type");
+		return -NLE_OBJ_MISMATCH;
 
 	if (!nl_list_empty(&obj->ce_list)) {
 		new = nl_object_clone(obj);
 		if (!new)
-			return nl_errno(ENOMEM);
+			return -NLE_NOMEM;
 	} else {
 		nl_object_get(obj);
 		new = obj;
@@ -331,7 +353,7 @@
 int nl_cache_move(struct nl_cache *cache, struct nl_object *obj)
 {
 	if (cache->c_ops->co_obj_ops != obj->ce_ops)
-		return nl_error(EINVAL, "Object mismatches cache type");
+		return -NLE_OBJ_MISMATCH;
 
 	NL_DBG(3, "Moving object %p to cache %p\n", obj, cache);
 	
@@ -405,7 +427,7 @@
 
 /**
  * Request a full dump from the kernel to fill a cache
- * @arg handle		Netlink handle
+ * @arg sk		Netlink socket.
  * @arg cache		Cache subjected to be filled.
  *
  * Send a dumping request to the kernel causing it to dump all objects
@@ -414,15 +436,15 @@
  * Use nl_cache_pickup() to read the objects from the socket and fill them
  * into a cache.
  */
-int nl_cache_request_full_dump(struct nl_handle *handle, struct nl_cache *cache)
+int nl_cache_request_full_dump(struct nl_sock *sk, struct nl_cache *cache)
 {
 	NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n",
 	          cache, nl_cache_name(cache));
 
 	if (cache->c_ops->co_request_update == NULL)
-		return nl_error(EOPNOTSUPP, "Operation not supported");
+		return -NLE_OPNOTSUPP;
 
-	return cache->c_ops->co_request_update(cache, handle);
+	return cache->c_ops->co_request_update(cache, sk);
 }
 
 /** @cond SKIP */
@@ -439,7 +461,7 @@
 }
 /** @endcond */
 
-int __cache_pickup(struct nl_handle *handle, struct nl_cache *cache,
+int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
 		   struct nl_parser_param *param)
 {
 	int err;
@@ -452,17 +474,17 @@
 	NL_DBG(1, "Picking up answer for cache %p <%s>...\n",
 		  cache, nl_cache_name(cache));
 
-	cb = nl_cb_clone(handle->h_cb);
+	cb = nl_cb_clone(sk->s_cb);
 	if (cb == NULL)
-		return nl_get_errno();
+		return -NLE_NOMEM;
 
 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x);
 
-	err = nl_recvmsgs(handle, cb);
+	err = nl_recvmsgs(sk, cb);
 	if (err < 0)
 		NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \
 		       "%d: %s", cache, nl_cache_name(cache),
-		       err, nl_geterror());
+		       err, nl_geterror(err));
 
 	nl_cb_put(cb);
 
@@ -476,7 +498,7 @@
 
 /**
  * Pickup a netlink dump response and put it into a cache.
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg cache		Cache to put items into.
  *
  * Waits for netlink messages to arrive, parses them and puts them into
@@ -484,18 +506,18 @@
  *
  * @return 0 on success or a negative error code.
  */
-int nl_cache_pickup(struct nl_handle *handle, struct nl_cache *cache)
+int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache)
 {
 	struct nl_parser_param p = {
 		.pp_cb = pickup_cb,
 		.pp_arg = cache,
 	};
 
-	return __cache_pickup(handle, cache, &p);
+	return __cache_pickup(sk, cache, &p);
 }
 
 static int cache_include(struct nl_cache *cache, struct nl_object *obj,
-			 struct nl_msgtype *type, change_func_t cb)
+			 struct nl_msgtype *type, change_func_t cb, void *data)
 {
 	struct nl_object *old;
 
@@ -507,7 +529,7 @@
 			nl_cache_remove(old);
 			if (type->mt_act == NL_ACT_DEL) {
 				if (cb)
-					cb(cache, old, NL_ACT_DEL);
+					cb(cache, old, NL_ACT_DEL, data);
 				nl_object_put(old);
 			}
 		}
@@ -515,10 +537,10 @@
 		if (type->mt_act == NL_ACT_NEW) {
 			nl_cache_move(cache, obj);
 			if (old == NULL && cb)
-				cb(cache, obj, NL_ACT_NEW);
+				cb(cache, obj, NL_ACT_NEW, data);
 			else if (old) {
 				if (nl_object_diff(old, obj) && cb)
-					cb(cache, obj, NL_ACT_CHANGE);
+					cb(cache, obj, NL_ACT_CHANGE, data);
 
 				nl_object_put(old);
 			}
@@ -533,36 +555,37 @@
 }
 
 int nl_cache_include(struct nl_cache *cache, struct nl_object *obj,
-		     change_func_t change_cb)
+		     change_func_t change_cb, void *data)
 {
 	struct nl_cache_ops *ops = cache->c_ops;
 	int i;
 
 	if (ops->co_obj_ops != obj->ce_ops)
-		return nl_error(EINVAL, "Object mismatches cache type");
+		return -NLE_OBJ_MISMATCH;
 
 	for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
 		if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype)
 			return cache_include(cache, obj, &ops->co_msgtypes[i],
-					     change_cb);
+					     change_cb, data);
 
-	return nl_errno(EINVAL);
+	return -NLE_MSGTYPE_NOSUPPORT;
 }
 
 static int resync_cb(struct nl_object *c, struct nl_parser_param *p)
 {
 	struct nl_cache_assoc *ca = p->pp_arg;
 
-	return nl_cache_include(ca->ca_cache, c, ca->ca_change);
+	return nl_cache_include(ca->ca_cache, c, ca->ca_change, ca->ca_change_data);
 }
 
-int nl_cache_resync(struct nl_handle *handle, struct nl_cache *cache,
-		    change_func_t change_cb)
+int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache,
+		    change_func_t change_cb, void *data)
 {
 	struct nl_object *obj, *next;
 	struct nl_cache_assoc ca = {
 		.ca_cache = cache,
 		.ca_change = change_cb,
+		.ca_change_data = data,
 	};
 	struct nl_parser_param p = {
 		.pp_cb = resync_cb,
@@ -575,17 +598,23 @@
 	/* Mark all objects so we can see if some of them are obsolete */
 	nl_cache_mark_all(cache);
 
-	err = nl_cache_request_full_dump(handle, cache);
+	err = nl_cache_request_full_dump(sk, cache);
 	if (err < 0)
 		goto errout;
 
-	err = __cache_pickup(handle, cache, &p);
+	err = __cache_pickup(sk, cache, &p);
 	if (err < 0)
 		goto errout;
 
-	nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list)
-		if (nl_object_is_marked(obj))
+	nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) {
+		if (nl_object_is_marked(obj)) {
+			nl_object_get(obj);
 			nl_cache_remove(obj);
+			if (change_cb)
+				change_cb(cache, obj, NL_ACT_DEL, data);
+			nl_object_put(obj);
+		}
+	}
 
 	NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache));
 
@@ -607,23 +636,19 @@
 {
 	int i, err;
 
-	if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) {
-		err = nl_error(EINVAL, "netlink message too short to be "
-				       "of kind %s", ops->co_name);
-		goto errout;
-	}
+	if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize))
+		return -NLE_MSG_TOOSHORT;
 
 	for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) {
 		if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) {
 			err = ops->co_msg_parser(ops, who, nlh, params);
-			if (err != -ENOENT)
+			if (err != -NLE_OPNOTSUPP)
 				goto errout;
 		}
 	}
 
 
-	err = nl_error(EINVAL, "Unsupported netlink message type %d",
-		       nlh->nlmsg_type);
+	err = -NLE_MSGTYPE_NOSUPPORT;
 errout:
 	return err;
 }
@@ -651,7 +676,7 @@
 
 /**
  * (Re)fill a cache with the contents in the kernel.
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg cache		cache to update
  *
  * Clears the specified cache and fills it with the current state in
@@ -659,11 +684,11 @@
  *
  * @return 0 or a negative error code.
  */
-int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache)
+int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
 {
 	int err;
 
-	err = nl_cache_request_full_dump(handle, cache);
+	err = nl_cache_request_full_dump(sk, cache);
 	if (err < 0)
 		return err;
 
@@ -671,7 +696,7 @@
 	       cache, nl_cache_name(cache));
 	nl_cache_clear(cache);
 
-	return nl_cache_pickup(handle, cache);
+	return nl_cache_pickup(sk, cache);
 }
 
 /** @} */
@@ -728,7 +753,7 @@
 			  struct nl_dump_params *params,
 			  struct nl_object *filter)
 {
-	int type = params ? params->dp_type : NL_DUMP_FULL;
+	int type = params ? params->dp_type : NL_DUMP_DETAILS;
 	struct nl_object_ops *ops;
 	struct nl_object *obj;
 
diff --git a/lib/cache_mngr.c b/lib/cache_mngr.c
index a144b92..81052aa 100644
--- a/lib/cache_mngr.c
+++ b/lib/cache_mngr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2007 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -95,7 +95,7 @@
 	if (nl_debug >= 4)
 		nl_object_dump(obj, &nl_debug_dp);
 #endif
-	return nl_cache_include(ca->ca_cache, obj, ca->ca_change);
+	return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
 }
 
 static int event_input(struct nl_msg *msg, void *arg)
@@ -140,56 +140,55 @@
 
 /**
  * Allocate new cache manager
- * @arg handle		Netlink socket/handle to be used
+ * @arg sk		Netlink socket.
  * @arg protocol	Netlink Protocol this manager is used for
  * @arg flags		Flags
  *
  * @return Newly allocated cache manager or NULL on failure.
  */
-struct nl_cache_mngr *nl_cache_mngr_alloc(struct nl_handle *handle,
-					  int protocol, int flags)
+int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
+			struct nl_cache_mngr **result)
 {
 	struct nl_cache_mngr *mngr;
+	int err = -NLE_NOMEM;
 
-	if (handle == NULL)
+	if (sk == NULL)
 		BUG();
 
 	mngr = calloc(1, sizeof(*mngr));
 	if (!mngr)
-		goto enomem;
+		goto errout;
 
-	mngr->cm_handle = handle;
+	mngr->cm_handle = sk;
 	mngr->cm_nassocs = 32;
 	mngr->cm_protocol = protocol;
 	mngr->cm_flags = flags;
 	mngr->cm_assocs = calloc(mngr->cm_nassocs,
 				 sizeof(struct nl_cache_assoc));
 	if (!mngr->cm_assocs)
-		goto enomem;
-
+		goto errout;
 
 	nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM,
 			    event_input, mngr);
 
 	/* Required to receive async event notifications */
-	nl_disable_sequence_check(mngr->cm_handle);
+	nl_socket_disable_seq_check(mngr->cm_handle);
 
-	if (nl_connect(mngr->cm_handle, protocol) < 0)
+	if ((err = nl_connect(mngr->cm_handle, protocol) < 0))
 		goto errout;
 
-	if (nl_socket_set_nonblocking(mngr->cm_handle) < 0)
+	if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0))
 		goto errout;
 
 	NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n",
 	       mngr, protocol, mngr->cm_nassocs);
 
-	return mngr;
+	*result = mngr;
+	return 0;
 
-enomem:
-	nl_errno(ENOMEM);
 errout:
 	nl_cache_mngr_free(mngr);
-	return NULL;
+	return err;
 }
 
 /**
@@ -197,6 +196,7 @@
  * @arg mngr		Cache manager.
  * @arg name		Name of cache to keep track of
  * @arg cb		Function to be called upon changes.
+ * @arg result		Pointer to store added cache.
  *
  * Allocates a new cache of the specified type and adds it to the manager.
  * The operation will trigger a full dump request from the kernel to
@@ -204,10 +204,10 @@
  * to the notification group of the cache to keep track of any further
  * changes.
  *
- * @return The newly allocated cache or NULL on failure.
+ * @return 0 on success or a negative error code.
  */
-struct nl_cache *nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
-				   change_func_t cb)
+int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
+		      change_func_t cb, void *data, struct nl_cache **result)
 {
 	struct nl_cache_ops *ops;
 	struct nl_cache *cache;
@@ -215,28 +215,19 @@
 	int err, i;
 
 	ops = nl_cache_ops_lookup(name);
-	if (!ops) {
-		nl_error(ENOENT, "Unknown cache type");
-		return NULL;
-	}
+	if (!ops)
+		return -NLE_NOCACHE;
 
-	if (ops->co_protocol != mngr->cm_protocol) {
-		nl_error(EINVAL, "Netlink protocol mismatch");
-		return NULL;
-	}
+	if (ops->co_protocol != mngr->cm_protocol)
+		return -NLE_PROTO_MISMATCH;
 
-	if (ops->co_groups == NULL) {
-		nl_error(EOPNOTSUPP, NULL);
-		return NULL;
-	}
+	if (ops->co_groups == NULL)
+		return -NLE_OPNOTSUPP;
 
-	for (i = 0; i < mngr->cm_nassocs; i++) {
+	for (i = 0; i < mngr->cm_nassocs; i++)
 		if (mngr->cm_assocs[i].ca_cache &&
-		    mngr->cm_assocs[i].ca_cache->c_ops == ops) {
-			nl_error(EEXIST, "Cache of this type already managed");
-			return NULL;
-		}
-	}
+		    mngr->cm_assocs[i].ca_cache->c_ops == ops)
+			return -NLE_EXIST;
 
 retry:
 	for (i = 0; i < mngr->cm_nassocs; i++)
@@ -248,10 +239,9 @@
 		mngr->cm_assocs = realloc(mngr->cm_assocs,
 					  mngr->cm_nassocs *
 					  sizeof(struct nl_cache_assoc));
-		if (mngr->cm_assocs == NULL) {
-			nl_errno(ENOMEM);
-			return NULL;
-		} else {
+		if (mngr->cm_assocs == NULL)
+			return -NLE_NOMEM;
+		else {
 			NL_DBG(1, "Increased capacity of cache manager %p " \
 				  "to %d\n", mngr, mngr->cm_nassocs);
 			goto retry;
@@ -259,10 +249,8 @@
 	}
 
 	cache = nl_cache_alloc(ops);
-	if (!cache) {
-		nl_errno(ENOMEM);
-		return NULL;
-	}
+	if (!cache)
+		return -NLE_NOMEM;
 
 	for (grp = ops->co_groups; grp->ag_group; grp++) {
 		err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
@@ -276,6 +264,7 @@
 
 	mngr->cm_assocs[i].ca_cache = cache;
 	mngr->cm_assocs[i].ca_change = cb;
+	mngr->cm_assocs[i].ca_change_data = data;
 
 	if (mngr->cm_flags & NL_AUTO_PROVIDE)
 		nl_cache_mngt_provide(cache);
@@ -283,7 +272,8 @@
 	NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
 	       cache, nl_cache_name(cache), mngr);
 
-	return cache;
+	*result = cache;
+	return 0;
 
 errout_drop_membership:
 	for (grp = ops->co_groups; grp->ag_group; grp++)
@@ -291,7 +281,7 @@
 errout_free_cache:
 	nl_cache_free(cache);
 
-	return NULL;
+	return err;
 }
 
 /**
@@ -334,7 +324,7 @@
 	ret = poll(&fds, 1, timeout);
 	NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
 	if (ret < 0)
-		return nl_errno(errno);
+		return -nl_syserr2nlerr(errno);
 
 	if (ret == 0)
 		return 0;
@@ -365,20 +355,24 @@
 }
 
 /**
- * Free cache manager
- * @arg mngr		Cache manager
+ * Free cache manager and all caches.
+ * @arg mngr		Cache manager.
  *
  * Release all resources after usage of a cache manager.
  */
 void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
 {
+	int i;
+
 	if (!mngr)
 		return;
 
-	if (mngr->cm_handle) {
+	if (mngr->cm_handle)
 		nl_close(mngr->cm_handle);
-		nl_handle_destroy(mngr->cm_handle);
-	}
+
+	for (i = 0; i < mngr->cm_nassocs; i++)
+		if (mngr->cm_assocs[i].ca_cache)
+			nl_cache_free(mngr->cm_assocs[i].ca_cache);
 
 	free(mngr->cm_assocs);
 	free(mngr);
diff --git a/lib/cache_mngt.c b/lib/cache_mngt.c
index de2bf24..d57d836 100644
--- a/lib/cache_mngt.c
+++ b/lib/cache_mngt.c
@@ -6,10 +6,11 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
+ * @ingroup core
  * @defgroup cache_mngt Caching
  * @{
  */
@@ -60,11 +61,14 @@
 	int i;
 	struct nl_cache_ops *ops;
 
-	for (ops = cache_ops; ops; ops = ops->co_next)
+	for (ops = cache_ops; ops; ops = ops->co_next) {
+		if (ops->co_protocol != protocol)
+			continue;
+
 		for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
-			if (ops->co_msgtypes[i].mt_id == msgtype &&
-			    ops->co_protocol == protocol)
+			if (ops->co_msgtypes[i].mt_id == msgtype)
 				return ops;
+	}
 
 	return NULL;
 }
@@ -126,15 +130,12 @@
  */
 int nl_cache_mngt_register(struct nl_cache_ops *ops)
 {
-	if (!ops->co_name)
-		return nl_error(EINVAL, "No cache name specified");
-
-	if (!ops->co_obj_ops)
-		return nl_error(EINVAL, "No obj cache ops specified");
+	if (!ops->co_name || !ops->co_obj_ops)
+		return -NLE_INVAL;
 
 	if (nl_cache_ops_lookup(ops->co_name))
-		return nl_error(EEXIST, "Cache operations already exist");
-	    
+		return -NLE_EXIST;
+
 	ops->co_next = cache_ops;
 	cache_ops = ops;
 
@@ -163,7 +164,7 @@
 			break;
 
 	if (!t)
-		return nl_error(ENOENT, "No such cache operations");
+		return -NLE_NOCACHE;
 
 	NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name);
 
diff --git a/lib/data.c b/lib/data.c
index 9399389..03cd9fe 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -6,11 +6,11 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
- * @ingroup utils
+ * @ingroup core
  * @defgroup data Abstract Data
  * @{
  */
@@ -56,11 +56,25 @@
 
 	return data;
 errout:
-	nl_errno(ENOMEM);
 	return NULL;
 }
 
 /**
+ * Allocate abstract data object based on netlink attribute.
+ * @arg nla		Netlink attribute of unspecific type.
+ *
+ * Allocates a new abstract data and copies the payload of the
+ * attribute to the abstract data object.
+ * 
+ * @see nla_data_alloc
+ * @return Newly allocated data handle or NULL
+ */
+struct nl_data *nl_data_alloc_attr(struct nlattr *nla)
+{
+	return nl_data_alloc(nla_data(nla), nla_len(nla));
+}
+
+/**
  * Clone an abstract data object.
  * @arg src		Abstract data object
  *
@@ -90,7 +104,7 @@
 	if (size > 0) {
 		data->d_data = realloc(data->d_data, data->d_size + size);
 		if (!data->d_data)
-			return nl_errno(ENOMEM);
+			return -NLE_NOMEM;
 
 		if (buf)
 			memcpy(data->d_data + data->d_size, buf, size);
diff --git a/lib/defs.h.in b/lib/defs.h.in
deleted file mode 100644
index ef86caa..0000000
--- a/lib/defs.h.in
+++ /dev/null
@@ -1,28 +0,0 @@
-/* lib/defs.h.in.  Generated from configure.in by autoheader.  */
-
-/* Define to the address where bug reports for this package should be sent. */
-#undef PACKAGE_BUGREPORT
-
-/* Define to the full name of this package. */
-#undef PACKAGE_NAME
-
-/* Define to the full name and version of this package. */
-#undef PACKAGE_STRING
-
-/* Define to the one symbol short name of this package. */
-#undef PACKAGE_TARNAME
-
-/* Define to the version of this package. */
-#undef PACKAGE_VERSION
-
-/* verbose errors */
-#undef VERBOSE_ERRORS
-
-/* Define to empty if `const' does not conform to ANSI C. */
-#undef const
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-#undef inline
-#endif
diff --git a/lib/doc.c b/lib/doc.c
index 8fee8c8..7e68bb3 100644
--- a/lib/doc.c
+++ b/lib/doc.c
@@ -6,12 +6,28 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
  * @mainpage
  *
+ * @section intro Introduction
+ *
+ * libnl is a set of libraries to deal with the netlink protocol and some
+ * of the high level protocols implemented on top of it. Its goal is to
+ * simplify netlink protocol usage and to create an abstraction layer using
+ * object based interfaces for various netlink based subsystems.The library
+ * was developed and tested on the 2.6.x kernel releases but it may work with
+ * older kernel series.
+ *
+ * @section toc Table of Contents
+ *
+ * - \subpage core_doc
+ * - \subpage route_doc
+ * - \subpage genl_doc
+ * - \subpage nf_doc
+ *
  * @section remarks Remarks
  *
  * @subsection cache_alloc Allocation of Caches
@@ -19,7 +35,7 @@
  * Almost all subsystem provide a function to allocate a new cache
  * of some form. The function usually looks like this:
  * @code
- * struct nl_cache *<object name>_alloc_cache(struct nl_handle *handle)
+ * struct nl_cache *<object name>_alloc_cache(struct nl_sock *sk);
  * @endcode
  *
  * These functions allocate a new cache for the own object type,
@@ -103,4 +119,366 @@
  * to the corresponding numeric value.
  *
  * @return Identifier as numeric value or a negative value if none was found.
+ *
+ * @page core_doc Core Library (-lnl)
+ * 
+ * @section core_intro Introduction
+ *
+ * The core library contains the fundamentals required to communicate over
+ * netlink sockets. It deals with connecting and unconnecting of sockets,
+ * sending and receiving of data, provides a customizeable receiving state
+ * machine, and provides a abstract data type framework which eases the
+ * implementation of object based netlink protocols where objects are added,
+ * removed, or modified with the help of netlink messages.
+ *
+ * @section core_toc Table of Contents
+ * 
+ * - \ref proto_fund
+ * - \ref sk_doc
+ * - \ref rxtx_doc
+ * - \ref cb_doc
+ *
+ * @section proto_fund Netlink Protocol Fundamentals
+ *
+ * The netlink protocol is a socket based IPC mechanism used for communication
+ * between userspace processes and the kernel. The netlink protocol uses the
+ * \c AF_NETLINK address family and defines a protocol type for each subsystem
+ * protocol (e.g. NETLINK_ROUTE, NETLINK_NETFILTER, etc). Its addressing
+ * schema is based on a 32 bit port number, formerly referred to as PID, which
+ * uniquely identifies each peer.
+ *
+ * The netlink protocol is based on messages each limited to the size of a
+ * memory page and consists of the netlink message header (struct nlmsghdr)
+ * plus the payload attached to it. The payload can consist of arbitary data
+ * but often contains a fixed sized family specifc header followed by a
+ * stream of \ref attr_doc. The use of attributes dramatically increases
+ * the flexibility of the protocol and allows for the protocol to be
+ * extended while maintaining backwards compatibility.
+ *
+ * The netlink message header (struct nlmsghdr):
+ * @code   
+ * 0                   1                   2                   3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-------------------------------------------------------------+
+ * |                          Length                             |
+ * +------------------------------+------------------------------+
+ * |            Type              |           Flags              |
+ * +------------------------------+------------------------------+
+ * |                      Sequence Number                        |
+ * +-------------------------------------------------------------+
+ * |                       Port (Address)                        |
+ * +-------------------------------------------------------------+
+ * @endcode
+ *
+ * Netlink differs between requests, notifications, and replies. Requests
+ * are messages which have the \c NLM_F_REQUEST flag set and are meant to
+ * request an action from the receiver. A request is typically sent from
+ * a userspace process to the kernel. Every request should be assigned a
+ * sequence number which should be incremented for each request sent on the
+ * sending side. Depending on the nature of the request, the receiver may
+ * reply to the request with regular netlink messages which should contain
+ * the same sequence number as the request it relates to. Notifications are
+ * of informal nature and don't expect a reply, therefore the sequence number
+ * is typically set to 0. It should be noted that unlike in protocols such as
+ * TCP there is no strict enforcment of the sequence number. The sole purpose
+ * of sequence numbers is to assist a sender in relating replies to the
+ * corresponding requests.
+ *
+ * @msc
+ * A,B;
+ * A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
+ * A<=B [label="PUT (seq=1)"];
+ * ...;
+ * A<=B [label="NOTIFY (seq=0)"];
+ * @endmsc
+ *
+ * If the size of a reply exceeds the size of a memory page and thus exceeds
+ * the maximum message size, the reply can be split into a series of multipart
+ * messages. A multipart message has the \c flag NLM_F_MULTI set and the
+ * receiver is expected to continue parsing the reply until the special
+ * message type \c NLMSG_DONE is received.
+ *
+ * @msc
+ * A,B;
+ * A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
+ * A<=B [label="PUT (seq=1, NLM_F_MULTI)"];
+ * ...;
+ * A<=B [label="PUT (seq=1, NLM_F_MULTI)"];
+ * A<=B [label="NLMSG_DONE (seq=1)"];
+ * @endmsc
+ *
+ * Errors can be reported using the standard message type \c NLMSG_ERROR which
+ * can carry an error code and the netlink mesage header of the request.
+ * Error messages should set their sequence number to the sequence number
+ * of the message which caused the error.
+ *
+ * @msc
+ * A,B;
+ * A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
+ * A<=B [label="NLMSG_ERROR code=EINVAL (seq=1)"];
+ * @endmsc
+ *
+ * The \c NLMSG_ERROR message type is also used to send acknowledge messages.
+ * An acknowledge message can be requested by setting the \c NLM_F_ACK flag
+ * message except that the error code is set to 0.
+ *
+ * @msc
+ * A,B;
+ * A=>B [label="GET (seq=1, NLM_F_REQUEST | NLM_F_ACK)"];
+ * A<=B [label="ACK (seq=1)"];
+ * @endmsc
+ *
+ * @section sk_doc Dealing with Netlink Sockets
+ *
+ * In order to use the netlink protocol, a netlink socket is required. Each
+ * socket defines a completely independent context for sending and receiving
+ * of messages. The netlink socket and all its related attributes are
+ * represented by struct nl_sock.
+ *
+ * @code
+ * nl_socket_alloc()                      Allocate new socket structure.
+ * nl_socket_free(s)                      Free socket structure.
+ * @endcode
+ *
+ * @subsection local_port Local Port
+ * The local port number uniquely identifies the socket and is used to
+ * address it. A unique local port is generated automatically when the socket
+ * is allocated. It will consist of the Process ID (22 bits) and a random
+ * number (10 bits) to allow up to 1024 sockets per process.
+ *
+ * @code
+ * nl_socket_get_local_port(sk)           Return the peer's port number.
+ * nl_socket_set_local_port(sk, port)     Set the peer's port number.
+ * @endcode
+ *
+ * @subsection peer_port Peer Port
+ * A peer port can be assigned to the socket which will result in all unicast
+ * messages sent over the socket to be addresses to the corresponding peer. If
+ * no peer is specified, the kernel will try to automatically bind the socket
+ * to a kernel side socket of the same netlink protocol family. It is common
+ * practice not to bind the socket to a peer port as typically only one kernel
+ * side socket exists per netlink protocol family.
+ *
+ * @code
+ * nl_socket_get_peer_port(sk)            Return the local port number.
+ * nl_socket_set_peer_port(sk, port)      Set the local port number.
+ * @endcode
+ *
+ * @subsection sock_fd File Descriptor
+ * The file descriptor of the socket(2).
+ *
+ * @code
+ * nl_socket_get_fd(sk)                   Return file descriptor.
+ * nl_socket_set_buffer_size(sk, rx, tx)  Set buffer size of socket.
+ * nl_socket_set_nonblocking(sk)          Set socket to non-blocking state.
+ * @endcode
+ *
+ * @subsection group_sub Group Subscriptions
+ * Each socket can subscribe to multicast groups of the netlink protocol
+ * family it is bound to. The socket will then receive a copy of each
+ * message sent to any of the groups. Multicast groups are commonly used
+ * to implement event notifications. Prior to kernel 2.6.14 the group
+ * subscription was performed using a bitmask which limited the number of
+ * groups per protocol family to 32. This outdated interface can still be
+ * accessed via the function nl_join_groups even though it is not recommended
+ * for new code. Starting with 2.6.14 a new method was introduced which
+ * supports subscribing to an almost unlimited number of multicast groups.
+ *
+ * @code
+ * nl_socket_add_membership(sk, group)    Become a member of a multicast group.
+ * nl_socket_drop_membership(sk, group)   Drop multicast group membership.
+ * nl_join_groups(sk, groupmask)          Join a multicast group (obsolete).
+ * @endcode
+ *
+ * @subsection seq_num Sequence Numbers
+ * The socket keeps track of the sequence numbers used. The library will
+ * automatically verify the sequence number of messages received unless
+ * the check was disabled using the function nl_socket_disable_seq_check().
+ * When a message is sent using nl_send_auto_complete(), the sequence number
+ * is automatically filled in, and replies will be verified correctly.
+ *
+ * @code
+ * nl_socket_disable_seq_check(sk)        Disable checking of sequece numbers.
+ * nl_socket_use_seq(sk)                  Use sequence number and bump to next.
+ * @endcode
+ *
+ * @subsection sock_cb Callback Configuration
+ * Every socket is associated a callback configuration which enables the
+ * applications to hook into various internal functions and control the
+ * receiving and sendings semantics. For more information, see section
+ * \ref cb_doc.
+ *
+ * @code
+ * nl_socket_alloc_cb(cb)                 Allocate socket based on callback set.
+ * nl_socket_get_cb(sk)                   Return callback configuration.
+ * nl_socket_set_cb(sk, cb)               Replace callback configuration.
+ * nl_socket_modify_cb(sk, ...)           Modify a specific callback function.
+ * @endcode
+ *
+ * @subsection sk_other Other Functions
+ * @code
+ * nl_socket_enable_auto_ack(sock)        Enable automatic request of ACK.
+ * nl_socket_disable_auto_ack(sock)       Disable automatic request of ACK.
+ * nl_socket_enable_msg_peek(sock)        Enable message peeking.
+ * nl_socket_disable_msg_peek(sock)       Disable message peeking.
+ * nl_socket_set_passcred(sk, state)      Enable/disable credential passing.
+ * nl_socket_recv_pktinfo(sk, state)      Enable/disable packet information.
+ * @endcode
+ *
+ * @section rxtx_doc Sending and Receiving of Data
+ *
+ * @subsection recv_semantisc Receiving Semantics
+ * @code
+ *          nl_recvmsgs_default(set)
+ *                 | cb = nl_socket_get_cb(sk)
+ *                 v
+ *          nl_recvmsgs(sk, cb)
+ *                 |           [Application provides nl_recvmsgs() replacement]
+ *                 |- - - - - - - - - - - - - - - v
+ *                 |                     cb->cb_recvmsgs_ow()
+ *                 |
+ *                 |               [Application provides nl_recv() replacement]
+ * +-------------->|- - - - - - - - - - - - - - - v
+ * |           nl_recv()                   cb->cb_recv_ow()
+ * |  +----------->|<- - - - - - - - - - - - - - -+
+ * |  |            v
+ * |  |      Parse Message
+ * |  |            |- - - - - - - - - - - - - - - v
+ * |  |            |                         NL_CB_MSG_IN()
+ * |  |            |<- - - - - - - - - - - - - - -+
+ * |  |            |
+ * |  |            |- - - - - - - - - - - - - - - v
+ * |  |      Sequence Check                NL_CB_SEQ_CHECK()
+ * |  |            |<- - - - - - - - - - - - - - -+
+ * |  |            |
+ * |  |            |- - - - - - - - - - - - - - - v  [ NLM_F_ACK is set ]
+ * |  |            |                      NL_CB_SEND_ACK()
+ * |  |            |<- - - - - - - - - - - - - - -+
+ * |  |            |
+ * |  |      +-----+------+--------------+----------------+--------------+
+ * |  |      v            v              v                v              v
+ * |  | Valid Message    ACK       NO-OP Message  End of Multipart     Error
+ * |  |      |            |              |                |              |
+ * |  |      v            v              v                v              v
+ * |  |NL_CB_VALID()  NL_CB_ACK()  NL_CB_SKIPPED()  NL_CB_FINISH()  cb->cb_err()
+ * |  |      |            |              |                |              |
+ * |  |      +------------+--------------+----------------+              v
+ * |  |                                  |                           (FAILURE)
+ * |  |                                  |  [Callback returned NL_SKIP]
+ * |  |  [More messages to be parsed]    |<-----------
+ * |  +----------------------------------|
+ * |                                     |
+ * |         [is Multipart message]      |
+ * +-------------------------------------|  [Callback returned NL_STOP]
+ *                                       |<-----------
+ *                                       v
+ *                                   (SUCCESS)
+ *
+ *                          At any time:
+ *                                Message Format Error
+ *                                         |- - - - - - - - - - - - v
+ *                                         v                  NL_CB_INVALID()
+ *                                     (FAILURE)
+ *
+ *                                Message Overrun (Kernel Lost Data)
+ *                                         |- - - - - - - - - - - - v
+ *                                         v                  NL_CB_OVERRUN()
+ *                                     (FAILURE)
+ *
+ *                                Callback returned negative error code
+ *                                     (FAILURE)
+ * @endcode
+ *
+ * @subsection send_semantics Sending Semantisc
+ *
+ * @code
+ *     nl_send_auto_complete(sk, msg)
+ *             | [Automatically completes netlink message header]
+ *             | [(local port, sequence number)                 ]
+ *             |
+ *             |                   [Application provies nl_send() replacement]
+ *             |- - - - - - - - - - - - - - - - - - - - v
+ *             v                                 cb->cb_send_ow()
+ *         nl_send(sk, msg)
+ *             | [If available, add peer port and credentials]
+ *             v
+ *        nl_sendmsg(sk, msg, msghdr)
+ *             |- - - - - - - - - - - - - - - - - - - - v
+ *             |                                 NL_CB_MSG_OUT()
+ *             |<- - - - - - - - - - - - - - - - - - - -+
+ *             v
+ *         sendmsg()
+ * @endcode
+ *
+ * @section cb_doc Callback Configurations
+ * Callbacks and overwriting capabilities are provided to control various
+ * semantics of the library. All callback functions are packed together in
+ * struct nl_cb which is attached to a netlink socket or passed on to 
+ * the respective functions directly.
+ *
+ * @subsection cb_ret_doc Callback Return Values
+ * Callback functions can control the flow of the calling layer by returning
+ * appropriate error codes:
+ * @code
+ * Action ID        | Description
+ * -----------------+-------------------------------------------------------
+ * NL_OK            | Proceed with whatever comes next.
+ * NL_SKIP          | Skip message currently being processed and continue
+ *                  | with next message.
+ * NL_STOP          | Stop parsing and discard all remaining messages in
+ *                  | this set of messages.
+ * @endcode
+ *
+ * All callbacks are optional and a default action is performed if no 
+ * application specific implementation is provided:
+ *
+ * @code
+ * Callback ID       | Default Return Value
+ * ------------------+----------------------
+ * NL_CB_VALID       | NL_OK
+ * NL_CB_FINISH      | NL_STOP
+ * NL_CB_OVERRUN     | NL_STOP
+ * NL_CB_SKIPPED     | NL_SKIP
+ * NL_CB_ACK         | NL_STOP
+ * NL_CB_MSG_IN      | NL_OK
+ * NL_CB_MSG_OUT     | NL_OK
+ * NL_CB_INVALID     | NL_STOP
+ * NL_CB_SEQ_CHECK   | NL_OK
+ * NL_CB_SEND_ACK    | NL_OK
+ *                   |
+ * Error Callback    | NL_STOP
+ * @endcode
+ *
+ * In order to simplify typical usages of the library, different sets of
+ * default callback implementations exist:
+ * @code
+ * NL_CB_DEFAULT: No additional actions
+ * NL_CB_VERBOSE: Automatically print warning and error messages to a file
+ *                descriptor as appropriate. This is useful for CLI based
+ *                applications.
+ * NL_CB_DEBUG:   Print informal debugging information for each message
+ *                received. This will result in every message beint sent or
+ *                received to be printed to the screen in a decoded,
+ *                human-readable format.
+ * @endcode
+ *
+ * @par 1) Setting up a callback set
+ * @code
+ * // Allocate a callback set and initialize it to the verbose default set
+ * struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE);
+ *
+ * // Modify the set to call my_func() for all valid messages
+ * nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL);
+ *
+ * // Set the error message handler to the verbose default implementation
+ * // and direct it to print all errors to the given file descriptor.
+ * FILE *file = fopen(...);
+ * nl_cb_err(cb, NL_CB_VERBOSE, NULL, file);
+ * @endcode
+ *
+ * @page route_doc Routing Family
+ *
+ * @page genl_doc Generic Netlink Family
+ *
+ * @page nf_doc Netfilter Subsystem
  */
diff --git a/lib/error.c b/lib/error.c
new file mode 100644
index 0000000..9a9fac7
--- /dev/null
+++ b/lib/error.c
@@ -0,0 +1,110 @@
+/*
+ * lib/error.c		Error Handling
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+
+static const char *errmsg[NLE_MAX+1] = {
+[NLE_SUCCESS]		= "Success",
+[NLE_FAILURE]		= "Unspecific failure",
+[NLE_INTR]		= "Interrupted system call",
+[NLE_BAD_SOCK]		= "Bad socket",
+[NLE_AGAIN]		= "Try again",
+[NLE_NOMEM]		= "Out of memory",
+[NLE_EXIST]		= "Object exists",
+[NLE_INVAL]		= "Invalid input data or parameter",
+[NLE_RANGE]		= "Input data out of range",
+[NLE_MSGSIZE]		= "Message size not sufficient",
+[NLE_OPNOTSUPP]		= "Operation not supported",
+[NLE_AF_NOSUPPORT]	= "Address family not supported",
+[NLE_OBJ_NOTFOUND]	= "Object not found",
+[NLE_NOATTR]		= "Attribute not available",
+[NLE_MISSING_ATTR]	= "Missing attribute",
+[NLE_AF_MISMATCH]	= "Address family mismatch",
+[NLE_SEQ_MISMATCH]	= "Message sequence number mismatch",
+[NLE_MSG_OVERFLOW]	= "Kernel reported message overflow",
+[NLE_MSG_TRUNC]		= "Kernel reported truncated message",
+[NLE_NOADDR]		= "Invalid address for specified address family",
+[NLE_SRCRT_NOSUPPORT]	= "Source based routing not supported",
+[NLE_MSG_TOOSHORT]	= "Netlink message is too short",
+[NLE_MSGTYPE_NOSUPPORT]	= "Netlink message type is not supported",
+[NLE_OBJ_MISMATCH]	= "Object type does not match cache",
+[NLE_NOCACHE]		= "Unknown or invalid cache type",
+[NLE_BUSY]		= "Object busy",
+[NLE_PROTO_MISMATCH]	= "Protocol mismatch",
+[NLE_NOACCESS]		= "No Access",
+[NLE_PERM]		= "Operation not permitted",
+[NLE_PKTLOC_FILE]	= "Unable to open packet location file",
+};
+
+/**
+ * Return error message for an error code
+ * @return error message
+ */
+const char *nl_geterror(int error)
+{
+	error = abs(error);
+
+	if (error > NLE_MAX)
+		error = NLE_FAILURE;
+
+	return errmsg[error];
+}
+
+/**
+ * Print a libnl error message
+ * @arg s		error message prefix
+ *
+ * Prints the error message of the call that failed last.
+ *
+ * If s is not NULL and *s is not a null byte the argument
+ * string is printed, followed by a colon and a blank. Then
+ * the error message and a new-line.
+ */
+void nl_perror(int error, const char *s)
+{
+	if (s && *s)
+		fprintf(stderr, "%s: %s\n", s, nl_geterror(error));
+	else
+		fprintf(stderr, "%s\n", nl_geterror(error));
+}
+
+int nl_syserr2nlerr(int error)
+{
+	error = abs(error);
+
+	switch (error) {
+	case EBADF:		return NLE_BAD_SOCK;
+	case EADDRINUSE:	return NLE_EXIST;
+	case EEXIST:		return NLE_EXIST;
+	case EADDRNOTAVAIL:	return NLE_NOADDR;
+	case ENOENT:		return NLE_OBJ_NOTFOUND;
+	case EINTR:		return NLE_INTR;
+	case EAGAIN:		return NLE_AGAIN;
+	case ENOTSOCK:		return NLE_BAD_SOCK;
+	case ENOPROTOOPT:	return NLE_INVAL;
+	case EFAULT:		return NLE_INVAL;
+	case EACCES:		return NLE_NOACCESS;
+	case EINVAL:		return NLE_INVAL;
+	case ENOBUFS:		return NLE_NOMEM;
+	case ENOMEM:		return NLE_NOMEM;
+	case EAFNOSUPPORT:	return NLE_AF_NOSUPPORT;
+	case EPROTONOSUPPORT:	return NLE_PROTO_MISMATCH;
+	case EOPNOTSUPP:	return NLE_OPNOTSUPP;
+	case EPERM:		return NLE_PERM;
+	case EBUSY:		return NLE_BUSY;
+	case ERANGE:		return NLE_RANGE;
+	default:		return NLE_FAILURE;
+	}
+}
+
+/** @} */
+
diff --git a/lib/family.c b/lib/family.c
deleted file mode 100644
index ba1d65f..0000000
--- a/lib/family.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * lib/family.c		Netlink Family
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-/**
- * @defgroup nlfam Netlink Families
- * @brief
- *
- * @{
- */
-
-#include <netlink-local.h>
-#include <netlink/netlink.h>
-#include <netlink/utils.h>
-
-/**
- * @name Netlink Family Name Translation
- * @{
- */
-
-static struct trans_tbl nlfamilies[] = {
-	__ADD(NETLINK_ROUTE,route)
-	__ADD(NETLINK_USERSOCK,usersock)
-	__ADD(NETLINK_FIREWALL,firewall)
-	__ADD(NETLINK_INET_DIAG,inetdiag)
-	__ADD(NETLINK_NFLOG,nflog)
-	__ADD(NETLINK_XFRM,xfrm)
-	__ADD(NETLINK_SELINUX,selinux)
-	__ADD(NETLINK_ISCSI,iscsi)
-	__ADD(NETLINK_AUDIT,audit)
-	__ADD(NETLINK_FIB_LOOKUP,fib_lookup)
-	__ADD(NETLINK_CONNECTOR,connector)
-	__ADD(NETLINK_NETFILTER,netfilter)
-	__ADD(NETLINK_IP6_FW,ip6_fw)
-	__ADD(NETLINK_DNRTMSG,dnrtmsg)
-	__ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent)
-	__ADD(NETLINK_GENERIC,generic)
-	__ADD(NETLINK_SCSITRANSPORT,scsitransport)
-	__ADD(NETLINK_ECRYPTFS,ecryptfs)
-};
-
-char * nl_nlfamily2str(int family, char *buf, size_t size)
-{
-	return __type2str(family, buf, size, nlfamilies,
-			  ARRAY_SIZE(nlfamilies));
-}
-
-int nl_str2nlfamily(const char *name)
-{
-	return __str2type(name, nlfamilies, ARRAY_SIZE(nlfamilies));
-}
-
-/** @} */
-
-/** @} */
diff --git a/lib/fib_lookup/lookup.c b/lib/fib_lookup/lookup.c
index 5766035..ce9c027 100644
--- a/lib/fib_lookup/lookup.c
+++ b/lib/fib_lookup/lookup.c
@@ -6,11 +6,10 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
- * @ingroup nlfam
  * @defgroup fib_lookup FIB Lookup
  * @brief
  * @{
@@ -63,7 +62,7 @@
 	if (src->fr_req)
 		if (!(dst->fr_req = (struct flnl_request *)
 				nl_object_clone(OBJ_CAST(src->fr_req))))
-			return nl_get_errno();
+			return -NLE_NOMEM;
 	
 	return 0;
 }
@@ -74,7 +73,7 @@
 	struct flnl_result *res;
 	struct fib_result_nl *fr;
 	struct nl_addr *addr;
-	int err = -EINVAL;
+	int err = -NLE_INVAL;
 
 	res = flnl_result_alloc();
 	if (!res)
@@ -121,27 +120,24 @@
 	return err;
 }
 
-static int result_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
+static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct flnl_result *res = (struct flnl_result *) obj;
 	char buf[128];
-	int line = 1;
 
-	dp_dump(p, "table %s prefixlen %u next-hop-selector %u\n",
+	nl_dump_line(p, "table %s prefixlen %u next-hop-selector %u\n",
 		rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)),
 		res->fr_prefixlen, res->fr_nh_sel);
-	dp_dump_line(p, line++, "type %s ",
+	nl_dump_line(p, "type %s ",
 		     nl_rtntype2str(res->fr_type, buf, sizeof(buf)));
-	dp_dump(p, "scope %s error %s (%d)\n",
+	nl_dump(p, "scope %s error %s (%d)\n",
 		rtnl_scope2str(res->fr_scope, buf, sizeof(buf)),
 		strerror(-res->fr_error), res->fr_error);
-
-	return line;
 }
 
-static int result_dump_full(struct nl_object *obj, struct nl_dump_params *p)
+static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p)
 {
-	return result_dump_brief(obj, p);
+	result_dump_line(obj, p);
 }
 
 static int result_compare(struct nl_object *_a, struct nl_object *_b,
@@ -209,7 +205,8 @@
  * @note Not all attributes can be changed, see
  *       \ref link_changeable "Changeable Attributes" for more details.
  */
-struct nl_msg *flnl_lookup_build_request(struct flnl_request *req, int flags)
+int flnl_lookup_build_request(struct flnl_request *req, int flags,
+			      struct nl_msg **result)
 {
 	struct nl_msg *msg;
 	struct nl_addr *addr;
@@ -228,30 +225,29 @@
 	fr.tb_id_in = table >= 0 ? table : RT_TABLE_UNSPEC;
 
 	addr = flnl_request_get_addr(req);
-	if (!addr) {
-		nl_error(EINVAL, "Request must specify the address");
-		return NULL;
-	}
+	if (!addr)
+		return -NLE_MISSING_ATTR;
 
 	fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr);
 
 	msg = nlmsg_alloc_simple(0, flags);
 	if (!msg)
-		goto errout;
+		return -NLE_NOMEM;
 
 	if (nlmsg_append(msg, &fr, sizeof(fr), NLMSG_ALIGNTO) < 0)
 		goto errout;
 
-	return msg;
+	*result = msg;
+	return 0;
 
 errout:
 	nlmsg_free(msg);
-	return NULL;
+	return -NLE_MSGSIZE;
 }
 
 /**
  * Perform FIB Lookup
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg req		Lookup request object.
  * @arg cache		Cache for result.
  *
@@ -260,22 +256,21 @@
  *
  * @return 0 on success or a negative error code.
  */
-int flnl_lookup(struct nl_handle *handle, struct flnl_request *req,
+int flnl_lookup(struct nl_sock *sk, struct flnl_request *req,
 		struct nl_cache *cache)
 {
 	struct nl_msg *msg;
 	int err;
 
-	msg = flnl_lookup_build_request(req, 0);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = flnl_lookup_build_request(req, 0, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
 	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	return nl_cache_pickup(handle, cache);
+	return nl_cache_pickup(sk, cache);
 }
 
 /** @} */
@@ -322,8 +317,10 @@
 	.oo_size		= sizeof(struct flnl_result),
 	.oo_free_data		= result_free_data,
 	.oo_clone		= result_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= result_dump_brief,
-	.oo_dump[NL_DUMP_FULL]	= result_dump_full,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= result_dump_line,
+	    [NL_DUMP_DETAILS]	= result_dump_details,
+	},
 	.oo_compare		= result_compare,
 };
 
diff --git a/lib/fib_lookup/request.c b/lib/fib_lookup/request.c
index 8b00224..ffcf8f5 100644
--- a/lib/fib_lookup/request.c
+++ b/lib/fib_lookup/request.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -48,11 +48,9 @@
 
 	if (src->lr_addr)
 		if (!(dst->lr_addr = nl_addr_clone(src->lr_addr)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
 static int request_compare(struct nl_object *_a, struct nl_object *_b,
@@ -152,7 +150,7 @@
 int flnl_request_set_addr(struct flnl_request *req, struct nl_addr *addr)
 {
 	if (addr->a_family != AF_INET)
-		return nl_error(EINVAL, "Address must be an IPv4 address");
+		return -NLE_AF_NOSUPPORT;
 
 	if (req->lr_addr)
 		nl_addr_put(req->lr_addr);
diff --git a/lib/genl/ctrl.c b/lib/genl/ctrl.c
index 9948a57..1301642 100644
--- a/lib/genl/ctrl.c
+++ b/lib/genl/ctrl.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -31,7 +31,7 @@
 static struct nl_cache_ops genl_ctrl_ops;
 /** @endcond */
 
-static int ctrl_request_update(struct nl_cache *c, struct nl_handle *h)
+static int ctrl_request_update(struct nl_cache *c, struct nl_sock *h)
 {
 	return genl_send_simple(h, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
 				CTRL_VERSION, NLM_F_DUMP);
@@ -61,17 +61,17 @@
 
 	family = genl_family_alloc();
 	if (family == NULL) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 
 	if (info->attrs[CTRL_ATTR_FAMILY_NAME] == NULL) {
-		err = nl_error(EINVAL, "Missing family name TLV");
+		err = -NLE_MISSING_ATTR;
 		goto errout;
 	}
 
 	if (info->attrs[CTRL_ATTR_FAMILY_ID] == NULL) {
-		err = nl_error(EINVAL, "Missing family id TLV");
+		err = -NLE_MISSING_ATTR;
 		goto errout;
 	}
 
@@ -111,7 +111,7 @@
 				goto errout;
 
 			if (tb[CTRL_ATTR_OP_ID] == NULL) {
-				err = nl_errno(EINVAL);
+				err = -NLE_MISSING_ATTR;
 				goto errout;
 			}
 			
@@ -128,11 +128,6 @@
 	}
 
 	err = pp->pp_cb((struct nl_object *) family, pp);
-	if (err < 0)
-		goto errout;
-
-	err = P_ACCEPT;
-
 errout:
 	genl_family_put(family);
 	return err;
@@ -143,20 +138,9 @@
  * @{
  */
 
-struct nl_cache *genl_ctrl_alloc_cache(struct nl_handle *handle)
+int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
 {
-	struct nl_cache * cache;
-	
-	cache = nl_cache_alloc(&genl_ctrl_ops);
-	if (cache == NULL)
-		return NULL;
-	
-	if (handle && nl_cache_refill(handle, cache) < 0) {
-		nl_cache_free(cache);
-		return NULL;
-	}
-
-	return cache;
+	return nl_cache_alloc_and_fill(&genl_ctrl_ops, sock, result);
 }
 
 /**
@@ -227,7 +211,7 @@
 
 /**
  * Resolve generic netlink family name to its identifier
- * @arg handle		Netlink Handle
+ * @arg sk		Netlink socket.
  * @arg name		Name of generic netlink family
  *
  * Resolves the generic netlink family name to its identifer and returns
@@ -235,19 +219,18 @@
  *
  * @return A positive identifier or a negative error code.
  */
-int genl_ctrl_resolve(struct nl_handle *handle, const char *name)
+int genl_ctrl_resolve(struct nl_sock *sk, const char *name)
 {
 	struct nl_cache *cache;
 	struct genl_family *family;
 	int err;
 
-	cache = genl_ctrl_alloc_cache(handle);
-	if (cache == NULL)
-		return nl_get_errno();
+	if ((err = genl_ctrl_alloc_cache(sk, &cache)) < 0)
+		return err;
 
 	family = genl_ctrl_search_by_name(cache, name);
 	if (family == NULL) {
-		err = nl_error(ENOENT, "Generic Netlink Family not found");
+		err = -NLE_OBJ_NOTFOUND;
 		goto errout;
 	}
 
diff --git a/lib/genl/family.c b/lib/genl/family.c
index e05b52c..4c6c18d 100644
--- a/lib/genl/family.c
+++ b/lib/genl/family.c
@@ -71,14 +71,12 @@
 	return 0;
 }
 
-static int family_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
+static void family_dump_line(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct genl_family *family = (struct genl_family *) obj;
 
-	dp_dump(p, "0x%04x %s version %u\n",
+	nl_dump(p, "0x%04x %s version %u\n",
 		family->gf_id, family->gf_name, family->gf_version);
-
-	return 1;
 }
 
 static struct trans_tbl ops_flags[] = {
@@ -93,13 +91,12 @@
 	return __flags2str(flags, buf, len, ops_flags, ARRAY_SIZE(ops_flags));
 }
 
-static int family_dump_full(struct nl_object *obj, struct nl_dump_params *p)
+static void family_dump_details(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct genl_family *family = (struct genl_family *) obj;
-	int line;
 
-	line = family_dump_brief(obj, p);
-	dp_dump_line(p, line++, "    hdrsize %u maxattr %u\n",
+	family_dump_line(obj, p);
+	nl_dump_line(p, "    hdrsize %u maxattr %u\n",
 		     family->gf_hdrsize, family->gf_maxattr);
 
 	if (family->ce_mask & FAMILY_ATTR_OPS) {
@@ -111,24 +108,21 @@
 
 			genl_op2name(family->gf_id, op->o_id, buf, sizeof(buf));
 
-			dp_dump_line(p, line++, "      op %s (0x%02x)",
-				     buf, op->o_id);
+			nl_dump_line(p, "      op %s (0x%02x)", buf, op->o_id);
 
 			if (op->o_flags)
-				dp_dump(p, " <%s>",
+				nl_dump(p, " <%s>",
 					ops_flags2str(op->o_flags, buf,
 						      sizeof(buf)));
 
-			dp_dump(p, "\n");
+			nl_dump(p, "\n");
 		}
 	}
-
-	return line;
 }
 
-static int family_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+static void family_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 {
-	return family_dump_full(obj, p);
+	family_dump_details(obj, p);
 }
 
 static int family_compare(struct nl_object *_a, struct nl_object *_b,
@@ -250,7 +244,7 @@
 
 	op = calloc(1, sizeof(*op));
 	if (op == NULL)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	op->o_id = id;
 	op->o_flags = flags;
@@ -270,13 +264,11 @@
 	.oo_constructor		= family_constructor,
 	.oo_free_data		= family_free_data,
 	.oo_clone		= family_clone,
-	.oo_dump[NL_DUMP_BRIEF] = family_dump_brief,
-	.oo_dump[NL_DUMP_FULL]  = family_dump_full,
-	.oo_dump[NL_DUMP_STATS] = family_dump_stats,
-#if 0
-	.oo_dump[NL_DUMP_XML]	= addr_dump_xml,
-	.oo_dump[NL_DUMP_ENV]	= addr_dump_env,
-#endif
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= family_dump_line,
+	    [NL_DUMP_DETAILS]	= family_dump_details,
+	    [NL_DUMP_STATS]	= family_dump_stats,
+	},
 	.oo_compare		= family_compare,
 	.oo_id_attrs		= FAMILY_ATTR_ID,
 };
diff --git a/lib/genl/genl.c b/lib/genl/genl.c
index 04cfebf..055be91 100644
--- a/lib/genl/genl.c
+++ b/lib/genl/genl.c
@@ -6,11 +6,10 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
- * @ingroup nlfam
  * @defgroup genl Generic Netlink
  *
  * @par Message Format
@@ -38,12 +37,12 @@
  * #include <netlink/genl/genl.h>
  * #include <netlink/genl/ctrl.h>
  *
- * struct nl_handle *sock;
+ * struct nl_sock *sock;
  * struct nl_msg *msg;
  * int family;
  *
  * // Allocate a new netlink socket
- * sock = nl_handle_alloc();
+ * sock = nl_socket_alloc();
  *
  * // Connect to generic netlink socket on kernel side
  * genl_connect(sock);
@@ -100,9 +99,9 @@
  * @{
  */
 
-int genl_connect(struct nl_handle *handle)
+int genl_connect(struct nl_sock *sk)
 {
-	return nl_connect(handle, NETLINK_GENERIC);
+	return nl_connect(sk, NETLINK_GENERIC);
 }
 
 /** @} */
@@ -114,7 +113,7 @@
 
 /**
  * Send trivial generic netlink message
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg family		Generic netlink family
  * @arg cmd		Command
  * @arg version		Version
@@ -125,7 +124,7 @@
  *
  * @return 0 on success or a negative error code.
  */
-int genl_send_simple(struct nl_handle *handle, int family, int cmd,
+int genl_send_simple(struct nl_sock *sk, int family, int cmd,
 		     int version, int flags)
 {
 	struct genlmsghdr hdr = {
@@ -133,7 +132,7 @@
 		.version = version,
 	};
 
-	return nl_send_simple(handle, family, flags, &hdr, sizeof(hdr));
+	return nl_send_simple(sk, family, flags, &hdr, sizeof(hdr));
 }
 
 /** @} */
@@ -164,7 +163,7 @@
 	struct genlmsghdr *ghdr;
 
 	if (!genlmsg_valid_hdr(nlh, hdrlen))
-		return nl_errno(EINVAL);
+		return -NLE_MSG_TOOSHORT;
 
 	ghdr = nlmsg_data(nlh);
 	return nla_validate(genlmsg_attrdata(ghdr, hdrlen),
@@ -177,7 +176,7 @@
 	struct genlmsghdr *ghdr;
 
 	if (!genlmsg_valid_hdr(nlh, hdrlen))
-		return nl_errno(EINVAL);
+		return -NLE_MSG_TOOSHORT;
 
 	ghdr = nlmsg_data(nlh);
 	return nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen),
diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c
index d737697..0ebe74d 100644
--- a/lib/genl/mngt.c
+++ b/lib/genl/mngt.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -108,12 +108,12 @@
 			goto found;
 	}
 
-	err = nl_errno(ENOENT);
+	err = -NLE_MSGTYPE_NOSUPPORT;
 	goto errout;
 
 found:
 	if (cmd->c_msg_parser == NULL)
-		err = nl_error(EOPNOTSUPP, "No message parser found.");
+		err = -NLE_OPNOTSUPP;
 	else {
 		struct nlattr *tb[cmd->c_maxattr + 1];
 		struct genl_info info = {
@@ -174,22 +174,17 @@
 	int err;
 
 	if (ops->co_protocol != NETLINK_GENERIC) {
-		err = nl_error(EINVAL, "cache operations not for protocol " \
-			       "NETLINK_GENERIC (protocol=%s)",
-			       ops->co_protocol);
+		err = -NLE_PROTO_MISMATCH;
 		goto errout;
 	}
 
 	if (ops->co_hdrsize < GENL_HDRSIZE(0)) {
-		err = nl_error(EINVAL, "co_hdrsize too short, probably " \
-			       "not including genlmsghdr, minsize=%d",
-			       GENL_HDRSIZE(0));
+		err = -NLE_INVAL;
 		goto errout;
 	}
 
 	if (ops->co_genl == NULL) {
-		err = nl_error(EINVAL, "co_genl is NULL, must provide " \
-			       "valid genl operations");
+		err = -NLE_INVAL;
 		goto errout;
 	}
 
@@ -236,20 +231,16 @@
 		return 0;
 	}
 
-	return nl_error(ENOENT, "Unable to find generic netlink family \"%s\"",
-			ops->o_name);
+	return -NLE_OBJ_NOTFOUND;
 }
 
-int genl_ops_resolve(struct nl_handle *handle, struct genl_ops *ops)
+int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops)
 {
 	struct nl_cache *ctrl;
 	int err;
 
-	ctrl = genl_ctrl_alloc_cache(handle);
-	if (ctrl == NULL) {
-		err = nl_get_errno();
+	if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
 		goto errout;
-	}
 
 	err = __genl_ops_resolve(ctrl, ops);
 
@@ -258,17 +249,14 @@
 	return err;
 }
 
-int genl_mngt_resolve(struct nl_handle *handle)
+int genl_mngt_resolve(struct nl_sock *sk)
 {
 	struct nl_cache *ctrl;
 	struct genl_ops *ops;
 	int err = 0;
 
-	ctrl = genl_ctrl_alloc_cache(handle);
-	if (ctrl == NULL) {
-		err = nl_get_errno();
+	if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
 		goto errout;
-	}
 
 	nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
 		err = __genl_ops_resolve(ctrl, ops);
diff --git a/lib/handlers.c b/lib/handlers.c
index 1797e4f..f13b89e 100644
--- a/lib/handlers.c
+++ b/lib/handlers.c
@@ -6,64 +6,14 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
- * @ingroup nl
+ * @ingroup core
  * @defgroup cb Callbacks/Customization
- * @brief
  *
- * Callbacks and overwriting capabilities are provided to take influence
- * in various control flows inside the library. All callbacks are packed
- * together in struct nl_cb which is then attached to a netlink socket or
- * passed on to the respective functions directly.
- *
- * Callbacks can control the flow of the underlying layer by returning
- * the appropriate error codes:
- * @code
- * Action ID        | Description
- * -----------------+-------------------------------------------------------
- * NL_OK       | Proceed with whatever comes next.
- * NL_SKIP          | Skip message currently being processed and continue
- *                  | with next message.
- * NL_STOP          | Stop parsing and discard all remaining messages in
- *                  | this set of messages.
- * @endcode
- *
- * All callbacks are optional and a default action is performed if no 
- * application specific implementation is provided:
- *
- * @code
- * Callback ID       | Default Return Value
- * ------------------+----------------------
- * NL_CB_VALID       | NL_OK
- * NL_CB_FINISH      | NL_STOP
- * NL_CB_OVERRUN     | NL_STOP
- * NL_CB_SKIPPED     | NL_SKIP
- * NL_CB_ACK         | NL_STOP
- * NL_CB_MSG_IN      | NL_OK
- * NL_CB_MSG_OUT     | NL_OK
- * NL_CB_INVALID     | NL_STOP
- * NL_CB_SEQ_CHECK   | NL_OK
- * NL_CB_SEND_ACK    | NL_OK
- *                   |
- * Error Callback    | NL_STOP
- * @endcode
- *
- * In order to simplify typical usages of the library, different sets of
- * default callback implementations exist:
- * @code
- * NL_CB_DEFAULT: No additional actions
- * NL_CB_VERBOSE: Automatically print warning and error messages to a file
- *                descriptor as appropriate. This is useful for CLI based
- *                applications.
- * NL_CB_DEBUG:   Print informal debugging information for each message
- *                received. This will result in every message beint sent or
- *                received to be printed to the screen in a decoded,
- *                human-readable format.
- * @endcode
- *
+ * @details
  * @par 1) Setting up a callback set
  * @code
  * // Allocate a callback set and initialize it to the verbose default set
@@ -140,7 +90,7 @@
 	print_header_content(ofd, &e->msg);
 	fprintf(ofd, "\n");
 
-	return e->error;
+	return -nl_syserr2nlerr(e->error);
 }
 
 static int nl_valid_handler_debug(struct nl_msg *msg, void *arg)
@@ -261,10 +211,8 @@
 		return NULL;
 
 	cb = calloc(1, sizeof(*cb));
-	if (!cb) {
-		nl_errno(ENOMEM);
+	if (!cb)
 		return NULL;
-	}
 
 	cb->cb_refcnt = 1;
 
@@ -338,10 +286,10 @@
 	      nl_recvmsg_msg_cb_t func, void *arg)
 {
 	if (type < 0 || type > NL_CB_TYPE_MAX)
-		return nl_error(ERANGE, "Callback type out of range");
+		return -NLE_RANGE;
 
 	if (kind < 0 || kind > NL_CB_KIND_MAX)
-		return nl_error(ERANGE, "Callback kind out of range");
+		return -NLE_RANGE;
 
 	if (kind == NL_CB_CUSTOM) {
 		cb->cb_set[type] = func;
@@ -388,7 +336,7 @@
 	      nl_recvmsg_err_cb_t func, void *arg)
 {
 	if (kind < 0 || kind > NL_CB_KIND_MAX)
-		return nl_error(ERANGE, "Callback kind out of range");
+		return -NLE_RANGE;
 
 	if (kind == NL_CB_CUSTOM) {
 		cb->cb_err = func;
@@ -414,7 +362,7 @@
  * @arg func		replacement callback for nl_recvmsgs()
  */
 void nl_cb_overwrite_recvmsgs(struct nl_cb *cb,
-			      int (*func)(struct nl_handle *, struct nl_cb *))
+			      int (*func)(struct nl_sock *, struct nl_cb *))
 {
 	cb->cb_recvmsgs_ow = func;
 }
@@ -425,7 +373,7 @@
  * @arg func		replacement callback for nl_recv()
  */
 void nl_cb_overwrite_recv(struct nl_cb *cb,
-			  int (*func)(struct nl_handle *, struct sockaddr_nl *,
+			  int (*func)(struct nl_sock *, struct sockaddr_nl *,
 				      unsigned char **, struct ucred **))
 {
 	cb->cb_recv_ow = func;
@@ -437,7 +385,7 @@
  * @arg func		replacement callback for nl_send()
  */
 void nl_cb_overwrite_send(struct nl_cb *cb,
-			  int (*func)(struct nl_handle *, struct nl_msg *))
+			  int (*func)(struct nl_sock *, struct nl_msg *))
 {
 	cb->cb_send_ow = func;
 }
diff --git a/lib/msg.c b/lib/msg.c
index 3d4fbc6..9fe9d54 100644
--- a/lib/msg.c
+++ b/lib/msg.c
@@ -6,11 +6,11 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
- * @ingroup nl
+ * @ingroup core
  * @defgroup msg Messages
  * Netlink Message Construction/Parsing Interface
  * 
@@ -284,7 +284,7 @@
  */
 int nlmsg_ok(const struct nlmsghdr *nlh, int remaining)
 {
-	return (remaining >= sizeof(struct nlmsghdr) &&
+	return (remaining >= (int)sizeof(struct nlmsghdr) &&
 		nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
 		nlh->nlmsg_len <= remaining);
 }
@@ -320,7 +320,7 @@
 		int maxtype, struct nla_policy *policy)
 {
 	if (!nlmsg_valid_hdr(nlh, hdrlen))
-		return nl_errno(EINVAL);
+		return -NLE_MSG_TOOSHORT;
 
 	return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
 			 nlmsg_attrlen(nlh, hdrlen), policy);
@@ -351,7 +351,7 @@
 		   struct nla_policy *policy)
 {
 	if (!nlmsg_valid_hdr(nlh, hdrlen))
-		return nl_errno(EINVAL);
+		return -NLE_MSG_TOOSHORT;
 
 	return nla_validate(nlmsg_attrdata(nlh, hdrlen),
 			    nlmsg_attrlen(nlh, hdrlen), maxtype, policy);
@@ -372,10 +372,14 @@
 	if (!nm)
 		goto errout;
 
-	nm->nm_nlh = calloc(1, len);
+	nm->nm_refcnt = 1;
+
+	nm->nm_nlh = malloc(len);
 	if (!nm->nm_nlh)
 		goto errout;
 
+	memset(nm->nm_nlh, 0, sizeof(struct nlmsghdr));
+
 	nm->nm_protocol = -1;
 	nm->nm_size = len;
 	nm->nm_nlh->nlmsg_len = nlmsg_total_size(0);
@@ -385,7 +389,6 @@
 	return nm;
 errout:
 	free(nm);
-	nl_errno(ENOMEM);
 	return NULL;
 }
 
@@ -517,10 +520,8 @@
 
 	tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len;
 
-	if ((tlen + nlmsg_len) > n->nm_size) {
-		nl_errno(ENOBUFS);
+	if ((tlen + nlmsg_len) > n->nm_size)
 		return NULL;
-	}
 
 	buf += nlmsg_len;
 	n->nm_nlh->nlmsg_len += tlen;
@@ -552,7 +553,7 @@
 
 	tmp = nlmsg_reserve(n, len, pad);
 	if (tmp == NULL)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	memcpy(tmp, data, len);
 	NL_DBG(2, "msg %p: Appended %zu bytes with padding %d\n", n, len, pad);
@@ -579,11 +580,11 @@
 	void *tmp;
 
 	if (newlen <= n->nm_size)
-		return nl_errno(EINVAL);
+		return -NLE_INVAL;
 
 	tmp = realloc(n->nm_nlh, newlen);
 	if (tmp == NULL)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	n->nm_nlh = tmp;
 	n->nm_size = newlen;
@@ -646,21 +647,39 @@
 }
 
 /**
- * Free a netlink message
- * @arg n		netlink message
- *
- * Destroys a netlink message and frees up all used memory.
- *
- * @pre The message must be unused.
+ * Acquire a reference on a netlink message
+ * @arg msg		message to acquire reference from
  */
-void nlmsg_free(struct nl_msg *n)
+void nlmsg_get(struct nl_msg *msg)
 {
-	if (!n)
+	msg->nm_refcnt++;
+	NL_DBG(4, "New reference to message %p, total %d\n",
+	       msg, msg->nm_refcnt);
+}
+
+/**
+ * Release a reference from an netlink message
+ * @arg msg		message to release reference from
+ *
+ * Frees memory after the last reference has been released.
+ */
+void nlmsg_free(struct nl_msg *msg)
+{
+	if (!msg)
 		return;
 
-	free(n->nm_nlh);
-	free(n);
-	NL_DBG(2, "msg %p: Freed\n", n);
+	msg->nm_refcnt--;
+	NL_DBG(4, "Returned message reference %p, %d remaining\n",
+	       msg, msg->nm_refcnt);
+
+	if (msg->nm_refcnt < 0)
+		BUG();
+
+	if (msg->nm_refcnt <= 0) {
+		free(msg->nm_nlh);
+		free(msg);
+		NL_DBG(2, "msg %p: Freed\n", msg);
+	}
 }
 
 /** @} */
@@ -821,8 +840,7 @@
 	ops = nl_cache_ops_associate(nlmsg_get_proto(msg),
 				     nlmsg_hdr(msg)->nlmsg_type);
 	if (ops == NULL)
-		return nl_error(ENOENT, "Unknown message type %d",
-				nlmsg_hdr(msg)->nlmsg_type);
+		return -NLE_MSGTYPE_NOSUPPORT;
 	p.pp_arg = &x;
 
 	return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
diff --git a/lib/netfilter/ct.c b/lib/netfilter/ct.c
index e9df924..9d61b6c 100644
--- a/lib/netfilter/ct.c
+++ b/lib/netfilter/ct.c
@@ -6,9 +6,10 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c= 2008 Patrick McHardy <kaber@trash.net>
  */
 
 /**
@@ -112,36 +113,36 @@
 		goto errout;
 
 	if (tb[CTA_IP_V4_SRC]) {
-		addr = nla_get_addr(tb[CTA_IP_V4_SRC], AF_INET);
+		addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET);
 		if (addr == NULL)
-			goto errout_errno;
+			goto errout_enomem;
 		err = nfnl_ct_set_src(ct, repl, addr);
 		nl_addr_put(addr);
 		if (err < 0)
 			goto errout;
 	}
 	if (tb[CTA_IP_V4_DST]) {
-		addr = nla_get_addr(tb[CTA_IP_V4_DST], AF_INET);
+		addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET);
 		if (addr == NULL)
-			goto errout_errno;
+			goto errout_enomem;
 		err = nfnl_ct_set_dst(ct, repl, addr);
 		nl_addr_put(addr);
 		if (err < 0)
 			goto errout;
 	}
 	if (tb[CTA_IP_V6_SRC]) {
-		addr = nla_get_addr(tb[CTA_IP_V6_SRC], AF_INET6);
+		addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6);
 		if (addr == NULL)
-			goto errout_errno;
+			goto errout_enomem;
 		err = nfnl_ct_set_src(ct, repl, addr);
 		nl_addr_put(addr);
 		if (err < 0)
 			goto errout;
 	}
 	if (tb[CTA_IP_V6_DST]) {
-		addr = nla_get_addr(tb[CTA_IP_V6_DST], AF_INET6);
+		addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6);
 		if (addr == NULL)
-			goto errout_errno;
+			goto errout_enomem;
 		err = nfnl_ct_set_dst(ct, repl, addr);
 		nl_addr_put(addr);
 		if (err < 0)
@@ -150,8 +151,8 @@
 
 	return 0;
 
-errout_errno:
-	return nl_get_errno();
+errout_enomem:
+	err = -NLE_NOMEM;
 errout:
 	return err;
 }
@@ -169,13 +170,13 @@
 		nfnl_ct_set_proto(ct, nla_get_u8(tb[CTA_PROTO_NUM]));
 	if (tb[CTA_PROTO_SRC_PORT])
 		nfnl_ct_set_src_port(ct, repl,
-				nla_get_u16(tb[CTA_PROTO_SRC_PORT]));
+			ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT])));
 	if (tb[CTA_PROTO_DST_PORT])
 		nfnl_ct_set_dst_port(ct, repl,
-				nla_get_u16(tb[CTA_PROTO_DST_PORT]));
+			ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT])));
 	if (tb[CTA_PROTO_ICMP_ID])
 		nfnl_ct_set_icmp_id(ct, repl,
-				nla_get_u16(tb[CTA_PROTO_ICMP_ID]));
+			ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID])));
 	if (tb[CTA_PROTO_ICMP_TYPE])
 		nfnl_ct_set_icmp_type(ct, repl,
 				nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]));
@@ -286,7 +287,7 @@
 	}
 }
 
-struct nfnl_ct *nfnlmsg_ct_parse(struct nlmsghdr *nlh)
+int nfnlmsg_ct_parse(struct nlmsghdr *nlh, struct nfnl_ct **result)
 {
 	struct nfnl_ct *ct;
 	struct nlattr *tb[CTA_MAX+1];
@@ -294,7 +295,7 @@
 
 	ct = nfnl_ct_alloc();
 	if (!ct)
-		return NULL;
+		return -NLE_NOMEM;
 
 	ct->ce_msgtype = nlh->nlmsg_type;
 
@@ -345,11 +346,12 @@
 			goto errout;
 	}
 
-	return ct;
+	*result = ct;
+	return 0;
 
 errout:
 	nfnl_ct_put(ct);
-	return NULL;
+	return err;
 }
 
 static int ct_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
@@ -358,34 +360,179 @@
 	struct nfnl_ct *ct;
 	int err;
 
-	ct = nfnlmsg_ct_parse(nlh);
-	if (ct == NULL)
-		goto errout_errno;
-
-	err = pp->pp_cb((struct nl_object *) ct, pp);
-	if (err < 0)
+	if ((err = nfnlmsg_ct_parse(nlh, &ct)) < 0)
 		goto errout;
 
-	err = P_ACCEPT;
-
+	err = pp->pp_cb((struct nl_object *) ct, pp);
 errout:
 	nfnl_ct_put(ct);
 	return err;
-
-errout_errno:
-	err = nl_get_errno();
-	goto errout;
 }
 
-int nfnl_ct_dump_request(struct nl_handle *h)
+int nfnl_ct_dump_request(struct nl_sock *sk)
 {
-	return nfnl_send_simple(h, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET,
+	return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET,
 				NLM_F_DUMP, AF_UNSPEC, 0);
 }
 
-static int ct_request_update(struct nl_cache *c, struct nl_handle *h)
+static int ct_request_update(struct nl_cache *cache, struct nl_sock *sk)
 {
-	return nfnl_ct_dump_request(h);
+	return nfnl_ct_dump_request(sk);
+}
+
+static int nfnl_ct_build_tuple(struct nl_msg *msg, const struct nfnl_ct *ct,
+			       int repl)
+{
+	struct nlattr *tuple, *ip, *proto;
+	struct nl_addr *addr;
+	int family;
+
+	family = nfnl_ct_get_family(ct);
+
+	tuple = nla_nest_start(msg, repl ? CTA_TUPLE_REPLY : CTA_TUPLE_ORIG);
+	if (!tuple)
+		goto nla_put_failure;
+
+	ip = nla_nest_start(msg, CTA_TUPLE_IP);
+	if (!ip)
+		goto nla_put_failure;
+
+	addr = nfnl_ct_get_src(ct, repl);
+	if (addr)
+		NLA_PUT_ADDR(msg,
+			     family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC,
+			     addr);
+
+	addr = nfnl_ct_get_dst(ct, repl);
+	if (addr)
+		NLA_PUT_ADDR(msg,
+			     family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST,
+			     addr);
+
+	nla_nest_end(msg, ip);
+
+	proto = nla_nest_start(msg, CTA_TUPLE_PROTO);
+	if (!proto)
+		goto nla_put_failure;
+
+	if (nfnl_ct_test_proto(ct))
+		NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_ct_get_proto(ct));
+
+	if (nfnl_ct_test_src_port(ct, repl))
+		NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT,
+			htons(nfnl_ct_get_src_port(ct, repl)));
+
+	if (nfnl_ct_test_dst_port(ct, repl))
+		NLA_PUT_U16(msg, CTA_PROTO_DST_PORT,
+			htons(nfnl_ct_get_dst_port(ct, repl)));
+
+	if (nfnl_ct_test_icmp_id(ct, repl))
+		NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
+			htons(nfnl_ct_get_icmp_id(ct, repl)));
+
+	if (nfnl_ct_test_icmp_type(ct, repl))
+		NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE,
+			    nfnl_ct_get_icmp_type(ct, repl));
+
+	if (nfnl_ct_test_icmp_code(ct, repl))
+		NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE,
+			    nfnl_ct_get_icmp_code(ct, repl));
+
+	nla_nest_end(msg, proto);
+
+	nla_nest_end(msg, tuple);
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+static int nfnl_ct_build_message(const struct nfnl_ct *ct, int cmd, int flags,
+				 struct nl_msg **result)
+{
+	struct nl_msg *msg;
+	int err;
+
+	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK, cmd, flags,
+				   nfnl_ct_get_family(ct), 0);
+	if (msg == NULL)
+		return -NLE_NOMEM;
+
+	if ((err = nfnl_ct_build_tuple(msg, ct, 0)) < 0)
+		goto err_out;
+
+	*result = msg;
+	return 0;
+
+err_out:
+	nlmsg_free(msg);
+	return err;
+}
+
+int nfnl_ct_build_add_request(const struct nfnl_ct *ct, int flags,
+			      struct nl_msg **result)
+{
+	return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_NEW, flags, result);
+}
+
+int nfnl_ct_add(struct nl_sock *sk, const struct nfnl_ct *ct, int flags)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_ct_build_add_request(ct, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(sk);
+}
+
+int nfnl_ct_build_delete_request(const struct nfnl_ct *ct, int flags,
+				 struct nl_msg **result)
+{
+	return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_DELETE, flags, result);
+}
+
+int nfnl_ct_del(struct nl_sock *sk, const struct nfnl_ct *ct, int flags)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_ct_build_delete_request(ct, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(sk);
+}
+
+int nfnl_ct_build_query_request(const struct nfnl_ct *ct, int flags,
+				struct nl_msg **result)
+{
+	return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_GET, flags, result);
+}
+
+int nfnl_ct_query(struct nl_sock *sk, const struct nfnl_ct *ct, int flags)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_ct_build_query_request(ct, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(sk);
 }
 
 /**
@@ -395,29 +542,17 @@
 
 /**
  * Build a conntrack cache holding all conntrack currently in the kernel
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
+ * @arg result		Pointer to store resulting cache.
  *
  * Allocates a new cache, initializes it properly and updates it to
  * contain all conntracks currently in the kernel.
  *
- * @note The caller is responsible for destroying and freeing the
- *       cache after using it.
- * @return The cache or NULL if an error has occured.
+ * @return 0 on success or a negative error code.
  */
-struct nl_cache *nfnl_ct_alloc_cache(struct nl_handle *handle)
+int nfnl_ct_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
 {
-	struct nl_cache *cache;
-
-	cache = nl_cache_alloc(&nfnl_ct_ops);
-	if (!cache)
-		return NULL;
-
-	if (handle && nl_cache_refill(handle, cache) < 0) {
-		free(cache);
-		return NULL;
-	}
-
-	return cache;
+	return nl_cache_alloc_and_fill(&nfnl_ct_ops, sk, result);
 }
 
 /** @} */
diff --git a/lib/netfilter/ct_obj.c b/lib/netfilter/ct_obj.c
index 0f5a79e..ae14c0d 100644
--- a/lib/netfilter/ct_obj.c
+++ b/lib/netfilter/ct_obj.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
  */
@@ -75,116 +75,199 @@
 	if (src->ct_orig.src) {
 		addr = nl_addr_clone(src->ct_orig.src);
 		if (!addr)
-			goto errout;
+			return -NLE_NOMEM;
 		dst->ct_orig.src = addr;
 	}
 
 	if (src->ct_orig.dst) {
 		addr = nl_addr_clone(src->ct_orig.dst);
 		if (!addr)
-			goto errout;
+			return -NLE_NOMEM;
 		dst->ct_orig.dst = addr;
 	}
 
 	if (src->ct_repl.src) {
 		addr = nl_addr_clone(src->ct_repl.src);
 		if (!addr)
-			goto errout;
+			return -NLE_NOMEM;
 		dst->ct_repl.src = addr;
 	}
 
 	if (src->ct_repl.dst) {
 		addr = nl_addr_clone(src->ct_repl.dst);
 		if (!addr)
-			goto errout;
+			return -NLE_NOMEM;
 		dst->ct_repl.dst = addr;
 	}
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
-static void ct_dump_dir(struct nfnl_ct *ct, int repl,
-			struct nl_dump_params *p)
+static void dump_addr(struct nl_dump_params *p, struct nl_addr *addr, int port)
 {
-	struct nl_addr *addr;
-	char addrbuf[64];
+	char buf[64];
 
-	addr = nfnl_ct_get_src(ct, repl);
 	if (addr)
-		dp_dump(p, "src=%s ",
-			nl_addr2str(addr, addrbuf, sizeof(addrbuf)));
+		nl_dump(p, "%s", nl_addr2str(addr, buf, sizeof(buf)));
 
-	addr = nfnl_ct_get_dst(ct, repl);
-	if (addr)
-		dp_dump(p, "dst=%s ",
-			nl_addr2str(addr, addrbuf, sizeof(addrbuf)));
+	if (port)
+		nl_dump(p, ":%u ", port);
+	else if (addr)
+		nl_dump(p, " ");
+}
 
-	if (nfnl_ct_test_src_port(ct, repl))
-		dp_dump(p, "sport=%u ", ntohs(nfnl_ct_get_src_port(ct, repl)));
-	if (nfnl_ct_test_dst_port(ct, repl))
-		dp_dump(p, "dport=%u ", ntohs(nfnl_ct_get_dst_port(ct, repl)));
+static void dump_icmp(struct nl_dump_params *p, struct nfnl_ct *ct, int reply)
+{
+	if (nfnl_ct_test_icmp_type(ct, reply))
+		nl_dump(p, "icmp type %d ", nfnl_ct_get_icmp_type(ct, reply));
 
-	if (nfnl_ct_test_icmp_type(ct, repl))
-		dp_dump(p, "type=%d ", nfnl_ct_get_icmp_type(ct, repl));
-	if (nfnl_ct_test_icmp_type(ct, repl))
-		dp_dump(p, "code=%d ", nfnl_ct_get_icmp_code(ct, repl));
-	if (nfnl_ct_test_icmp_type(ct, repl))
-		dp_dump(p, "id=%d ", ntohs(nfnl_ct_get_icmp_id(ct, repl)));
+	if (nfnl_ct_test_icmp_type(ct, reply))
+		nl_dump(p, "code %d ", nfnl_ct_get_icmp_code(ct, reply));
 
-	if (nfnl_ct_test_packets(ct, repl))
-		dp_dump(p, "packets=%llu ", nfnl_ct_get_packets(ct, repl));
-	if (nfnl_ct_test_bytes(ct, repl))
-		dp_dump(p, "bytes=%llu ", nfnl_ct_get_bytes(ct, repl));
+	if (nfnl_ct_test_icmp_type(ct, reply))
+		nl_dump(p, "id %d ", nfnl_ct_get_icmp_id(ct, reply));
+}
+
+static void ct_dump_tuples(struct nfnl_ct *ct, struct nl_dump_params *p)
+{
+	struct nl_addr *orig_src, *orig_dst, *reply_src, *reply_dst;
+	int orig_sport = 0, orig_dport = 0, reply_sport = 0, reply_dport = 0;
+	int sync = 0;
+
+	orig_src = nfnl_ct_get_src(ct, 0);
+	orig_dst = nfnl_ct_get_dst(ct, 0);
+	reply_src = nfnl_ct_get_src(ct, 1);
+	reply_dst = nfnl_ct_get_dst(ct, 1);
+
+	if (nfnl_ct_test_src_port(ct, 0))
+		orig_sport = nfnl_ct_get_src_port(ct, 0);
+
+	if (nfnl_ct_test_dst_port(ct, 0))
+		orig_dport = nfnl_ct_get_dst_port(ct, 0);
+
+	if (nfnl_ct_test_src_port(ct, 1))
+		reply_sport = nfnl_ct_get_src_port(ct, 1);
+
+	if (nfnl_ct_test_dst_port(ct, 1))
+		reply_dport = nfnl_ct_get_dst_port(ct, 1);
+
+	if (orig_src && orig_dst && reply_src && reply_dst &&
+	    orig_sport == reply_dport && orig_dport == reply_sport &&
+	    !nl_addr_cmp(orig_src, reply_dst) &&
+	    !nl_addr_cmp(orig_dst, reply_src))
+		sync = 1;
+
+	dump_addr(p, orig_src, orig_sport);
+	nl_dump(p, sync ? "<-> " : "-> ");
+	dump_addr(p, orig_dst, orig_dport);
+	dump_icmp(p, ct, 0);
+
+	if (!sync) {
+		dump_addr(p, reply_src, reply_sport);
+		nl_dump(p, "<- ");
+		dump_addr(p, reply_dst, reply_dport);
+		dump_icmp(p, ct, 1);
+	}
 }
 
 /* Compatible with /proc/net/nf_conntrack */
-static int ct_dump(struct nl_object *a, struct nl_dump_params *p)
+static void ct_dump_line(struct nl_object *a, struct nl_dump_params *p)
 {
 	struct nfnl_ct *ct = (struct nfnl_ct *) a;
 	char buf[64];
-	uint32_t status;
-	uint8_t family;
-	uint8_t proto;
 
-	family = nfnl_ct_get_family(ct);
-	dp_dump(p, "%-8s %u ", nl_af2str(family, buf, sizeof(buf)), family);
+	nl_new_line(p);
 
-	if (nfnl_ct_test_proto(ct)) {
-		proto = nfnl_ct_get_proto(ct);
-		dp_dump(p, "%-8s %u ",
-			nl_ip_proto2str(proto, buf, sizeof(buf)), proto);
-	}
-
-	if (nfnl_ct_test_timeout(ct))
-		dp_dump(p, "%ld ", nfnl_ct_get_timeout(ct));
+	if (nfnl_ct_test_proto(ct))
+		nl_dump(p, "%s ",
+		  nl_ip_proto2str(nfnl_ct_get_proto(ct), buf, sizeof(buf)));
 
 	if (nfnl_ct_test_tcp_state(ct))
-		dp_dump(p, "%s ",
+		nl_dump(p, "%s ",
 			nfnl_ct_tcp_state2str(nfnl_ct_get_tcp_state(ct),
 					      buf, sizeof(buf)));
 
-	ct_dump_dir(ct, 0, p);
+	ct_dump_tuples(ct, p);
 
-	status = nfnl_ct_get_status(ct);
-	if (!(status & IPS_SEEN_REPLY))
-		dp_dump(p, "[UNREPLIED] ");
+	if (nfnl_ct_test_mark(ct) && nfnl_ct_get_mark(ct))
+		nl_dump(p, "mark %u ", nfnl_ct_get_mark(ct));
 
-	ct_dump_dir(ct, 1, p);
+	nl_dump(p, "\n");
+}
 
-	if (status & IPS_ASSURED)
-		dp_dump(p, "[ASSURED] ");
+static void ct_dump_details(struct nl_object *a, struct nl_dump_params *p)
+{
+	struct nfnl_ct *ct = (struct nfnl_ct *) a;
+	char buf[64];
+	int fp = 0;
 
-	if (nfnl_ct_test_mark(ct))
-		dp_dump(p, "mark=%u ", nfnl_ct_get_mark(ct));
+	ct_dump_line(a, p);
+
+	nl_dump(p, "    id 0x%x ", ct->ct_id);
+	nl_dump_line(p, "family %s ",
+		nl_af2str(ct->ct_family, buf, sizeof(buf)));
 
 	if (nfnl_ct_test_use(ct))
-		dp_dump(p, "use=%u ", nfnl_ct_get_use(ct));
+		nl_dump(p, "refcnt %u ", nfnl_ct_get_use(ct));
 
-	dp_dump(p, "\n");
+	if (nfnl_ct_test_timeout(ct)) {
+		uint64_t timeout_ms = nfnl_ct_get_timeout(ct) * 1000UL;
+		nl_dump(p, "timeout %s ",
+			nl_msec2str(timeout_ms, buf, sizeof(buf)));
+	}
 
-	return 1;
+	if (ct->ct_status)
+		nl_dump(p, "<");
+
+#define PRINT_FLAG(str) \
+	{ nl_dump(p, "%s%s", fp++ ? "," : "", (str)); }
+
+	if (ct->ct_status & IPS_EXPECTED)
+		PRINT_FLAG("EXPECTED");
+	if (!(ct->ct_status & IPS_SEEN_REPLY))
+		PRINT_FLAG("NOREPLY");
+	if (ct->ct_status & IPS_ASSURED)
+		PRINT_FLAG("ASSURED");
+	if (!(ct->ct_status & IPS_CONFIRMED))
+		PRINT_FLAG("NOTSENT");
+	if (ct->ct_status & IPS_SRC_NAT)
+		PRINT_FLAG("SNAT");
+	if (ct->ct_status & IPS_DST_NAT)
+		PRINT_FLAG("DNAT");
+	if (ct->ct_status & IPS_SEQ_ADJUST)
+		PRINT_FLAG("SEQADJUST");
+	if (!(ct->ct_status & IPS_SRC_NAT_DONE))
+		PRINT_FLAG("SNAT_INIT");
+	if (!(ct->ct_status & IPS_DST_NAT_DONE))
+		PRINT_FLAG("DNAT_INIT");
+	if (ct->ct_status & IPS_DYING)
+		PRINT_FLAG("DYING");
+	if (ct->ct_status & IPS_FIXED_TIMEOUT)
+		PRINT_FLAG("FIXED_TIMEOUT");
+#undef PRINT_FLAG
+
+	if (ct->ct_status)
+		nl_dump(p, ">");
+	nl_dump(p, "\n");
+}
+
+static void ct_dump_stats(struct nl_object *a, struct nl_dump_params *p)
+{
+	struct nfnl_ct *ct = (struct nfnl_ct *) a;
+	double res;
+	char *unit;
+
+	ct_dump_details(a, p);
+
+	nl_dump_line(p, "        # packets      volume\n");
+
+	res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 1), &unit);
+	nl_dump_line(p, "    rx %10llu %7.2f %s\n",
+		nfnl_ct_get_packets(ct, 1), res, unit);
+
+	res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 0), &unit);
+	nl_dump_line(p, "    tx %10llu %7.2f %s\n",
+		nfnl_ct_get_packets(ct, 0), res, unit);
 }
 
 static int ct_compare(struct nl_object *_a, struct nl_object *_b,
@@ -197,7 +280,7 @@
 #define CT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, CT_ATTR_##ATTR, a, b, EXPR)
 #define CT_DIFF_VAL(ATTR, FIELD) CT_DIFF(ATTR, a->FIELD != b->FIELD)
 #define CT_DIFF_ADDR(ATTR, FIELD) \
-	((flags & LOOSE_FLAG_COMPARISON) \
+	((flags & LOOSE_COMPARISON) \
 		? CT_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \
 		: CT_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD)))
 
@@ -227,7 +310,7 @@
 	diff |= CT_DIFF_VAL(REPL_PACKETS,	ct_repl.packets);
 	diff |= CT_DIFF_VAL(REPL_BYTES,		ct_repl.bytes);
 
-	if (flags & LOOSE_FLAG_COMPARISON)
+	if (flags & LOOSE_COMPARISON)
 		diff |= CT_DIFF(STATUS, (a->ct_status ^ b->ct_status) &
 					b->ct_status_mask);
 	else
@@ -389,6 +472,31 @@
 	return ct->ct_status;
 }
 
+static struct trans_tbl status_flags[] = {
+	__ADD(IPS_EXPECTED, expected)
+	__ADD(IPS_SEEN_REPLY, seen_reply)
+	__ADD(IPS_ASSURED, assured)
+	__ADD(IPS_CONFIRMED, confirmed)
+	__ADD(IPS_SRC_NAT, snat)
+	__ADD(IPS_DST_NAT, dnat)
+	__ADD(IPS_SEQ_ADJUST, seqadjust)
+	__ADD(IPS_SRC_NAT_DONE, snat_done)
+	__ADD(IPS_DST_NAT_DONE, dnat_done)
+	__ADD(IPS_DYING, dying)
+	__ADD(IPS_FIXED_TIMEOUT, fixed_timeout)
+};
+
+char * nfnl_ct_status2str(int flags, char *buf, size_t len)
+{
+	return __flags2str(flags, buf, len, status_flags,
+			   ARRAY_SIZE(status_flags));
+}
+
+int nfnl_ct_str2status(const char *name)
+{
+	return __str2flags(name, status_flags, ARRAY_SIZE(status_flags));
+}
+
 void nfnl_ct_set_timeout(struct nfnl_ct *ct, uint32_t timeout)
 {
 	ct->ct_timeout = timeout;
@@ -458,7 +566,7 @@
 {
 	if (ct->ce_mask & CT_ATTR_FAMILY) {
 		if (addr->a_family != ct->ct_family)
-			return nl_error(EINVAL, "Address family mismatch");
+			return -NLE_AF_MISMATCH;
 	} else
 		nfnl_ct_set_family(ct, addr->a_family);
 
@@ -665,9 +773,11 @@
 	.oo_size		= sizeof(struct nfnl_ct),
 	.oo_free_data		= ct_free_data,
 	.oo_clone		= ct_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= ct_dump,
-	.oo_dump[NL_DUMP_FULL]	= ct_dump,
-	.oo_dump[NL_DUMP_STATS]	= ct_dump,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= ct_dump_line,
+	    [NL_DUMP_DETAILS]	= ct_dump_details,
+	    [NL_DUMP_STATS]	= ct_dump_stats,
+	},
 	.oo_compare		= ct_compare,
 	.oo_attrs2str		= ct_attrs2str,
 };
diff --git a/lib/netfilter/log.c b/lib/netfilter/log.c
index a6bf3d5..96ae6c5 100644
--- a/lib/netfilter/log.c
+++ b/lib/netfilter/log.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
  */
@@ -26,172 +26,13 @@
 #include <netlink/netfilter/nfnl.h>
 #include <netlink/netfilter/log.h>
 
-static struct nl_cache_ops nfnl_log_ops;
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-static uint64_t ntohll(uint64_t x)
-{
-	return x;
-}
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
-static uint64_t ntohll(uint64_t x)
-{
-	return __bswap_64(x);
-}
-#endif
-
-static struct nla_policy log_policy[NFULA_MAX+1] = {
-	[NFULA_PACKET_HDR]		= {
-		.minlen = sizeof(struct nfulnl_msg_packet_hdr)
-	},
-	[NFULA_MARK]			= { .type = NLA_U32 },
-	[NFULA_TIMESTAMP]		= {
-		.minlen = sizeof(struct nfulnl_msg_packet_timestamp)
-	},
-	[NFULA_IFINDEX_INDEV]		= { .type = NLA_U32 },
-	[NFULA_IFINDEX_OUTDEV]		= { .type = NLA_U32 },
-	[NFULA_IFINDEX_PHYSINDEV]	= { .type = NLA_U32 },
-	[NFULA_IFINDEX_PHYSOUTDEV]	= { .type = NLA_U32 },
-	[NFULA_HWADDR]			= {
-		.minlen = sizeof(struct nfulnl_msg_packet_hw)
-	},
-	//[NFULA_PAYLOAD]
-	[NFULA_PREFIX]			= { .type = NLA_STRING, },
-	[NFULA_UID]			= { .type = NLA_U32 },
-	[NFULA_SEQ]			= { .type = NLA_U32 },
-	[NFULA_SEQ_GLOBAL]		= { .type = NLA_U32 },
-};
-
-struct nfnl_log *nfnlmsg_log_parse(struct nlmsghdr *nlh)
-{
-	struct nfnl_log *log;
-	struct nlattr *tb[NFULA_MAX+1];
-	struct nlattr *attr;
-	int err;
-
-	log = nfnl_log_alloc();
-	if (!log)
-		return NULL;
-
-	log->ce_msgtype = nlh->nlmsg_type;
-
-	err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFULA_MAX,
-			  log_policy);
-	if (err < 0)
-		goto errout;
-
-	nfnl_log_set_family(log, nfnlmsg_family(nlh));
-
-	attr = tb[NFULA_PACKET_HDR];
-	if (attr) {
-		struct nfulnl_msg_packet_hdr *hdr = nla_data(attr);
-
-		nfnl_log_set_hwproto(log, hdr->hw_protocol);
-		nfnl_log_set_hook(log, hdr->hook);
-	}
-
-	attr = tb[NFULA_MARK];
-	if (attr)
-		nfnl_log_set_mark(log, ntohl(nla_get_u32(attr)));
-
-	attr = tb[NFULA_TIMESTAMP];
-	if (attr) {
-		struct nfulnl_msg_packet_timestamp *timestamp = nla_data(attr);
-		struct timeval tv;
-
-		tv.tv_sec = ntohll(timestamp->sec);
-		tv.tv_usec = ntohll(timestamp->usec);
-		nfnl_log_set_timestamp(log, &tv);
-	}
-
-	attr = tb[NFULA_IFINDEX_INDEV];
-	if (attr)
-		nfnl_log_set_indev(log, ntohl(nla_get_u32(attr)));
-
-	attr = tb[NFULA_IFINDEX_OUTDEV];
-	if (attr)
-		nfnl_log_set_outdev(log, ntohl(nla_get_u32(attr)));
-
-	attr = tb[NFULA_IFINDEX_PHYSINDEV];
-	if (attr)
-		nfnl_log_set_physindev(log, ntohl(nla_get_u32(attr)));
-
-	attr = tb[NFULA_IFINDEX_PHYSOUTDEV];
-	if (attr)
-		nfnl_log_set_physoutdev(log, ntohl(nla_get_u32(attr)));
-
-	attr = tb[NFULA_HWADDR];
-	if (attr) {
-		struct nfulnl_msg_packet_hw *hw = nla_data(attr);
-
-		nfnl_log_set_hwaddr(log, hw->hw_addr, ntohs(hw->hw_addrlen));
-	}
-
-	attr = tb[NFULA_PAYLOAD];
-	if (attr) {
-		err = nfnl_log_set_payload(log, nla_data(attr), nla_len(attr));
-		if (err < 0)
-			goto errout;
-	}
-
-	attr = tb[NFULA_PREFIX];
-	if (attr) {
-		err = nfnl_log_set_prefix(log, nla_data(attr));
-		if (err < 0)
-			goto errout;
-	}
-
-	attr = tb[NFULA_UID];
-	if (attr)
-		nfnl_log_set_uid(log, ntohl(nla_get_u32(attr)));
-
-	attr = tb[NFULA_SEQ];
-	if (attr)
-		nfnl_log_set_seq(log, ntohl(nla_get_u32(attr)));
-
-	attr = tb[NFULA_SEQ_GLOBAL];
-	if (attr)
-		nfnl_log_set_seq_global(log, ntohl(nla_get_u32(attr)));
-
-	return log;
-
-errout:
-	nfnl_log_put(log);
-	return NULL;
-}
-
-static int log_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
-			 struct nlmsghdr *nlh, struct nl_parser_param *pp)
-{
-	struct nfnl_log *log;
-	int err;
-
-	log = nfnlmsg_log_parse(nlh);
-	if (log == NULL)
-		goto errout_errno;
-
-	err = pp->pp_cb((struct nl_object *) log, pp);
-	if (err < 0)
-		goto errout;
-
-	err = P_ACCEPT;
-
-errout:
-	nfnl_log_put(log);
-	return err;
-
-errout_errno:
-	err = nl_get_errno();
-	goto errout;
-}
-
 /**
  * @name Log Commands
  * @{
  */
 
-static struct nl_msg *build_log_cmd_msg(uint8_t family, uint16_t queuenum,
-					uint8_t command)
+static int build_log_cmd_request(uint8_t family, uint16_t queuenum,
+				 uint8_t command, struct nl_msg **result)
 {
 	struct nl_msg *msg;
 	struct nfulnl_msg_config_cmd cmd;
@@ -199,142 +40,202 @@
 	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0,
 				   family, queuenum);
 	if (msg == NULL)
-		return NULL;
+		return -NLE_NOMEM;
 
 	cmd.command = command;
 	if (nla_put(msg, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0)
 		goto nla_put_failure;
 
-	return msg;
+	*result = msg;
+	return 0;
 
 nla_put_failure:
 	nlmsg_free(msg);
-	return NULL;
+	return -NLE_MSGSIZE;
 }
 
-static int send_log_msg(struct nl_handle *handle, struct nl_msg *msg)
+static int send_log_request(struct nl_sock *sk, struct nl_msg *msg)
 {
 	int err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
 	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
-struct nl_msg *nfnl_log_build_bind(uint16_t queuenum)
+int nfnl_log_build_pf_bind(uint8_t pf, struct nl_msg **result)
 {
-	return build_log_cmd_msg(0, queuenum, NFULNL_CFG_CMD_BIND);
+	return build_log_cmd_request(pf, 0, NFULNL_CFG_CMD_PF_BIND, result);
 }
 
-int nfnl_log_bind(struct nl_handle *nlh, uint16_t queuenum)
+int nfnl_log_pf_bind(struct nl_sock *nlh, uint8_t pf)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_log_build_pf_bind(pf, &msg)) < 0)
+		return err;
+
+	return send_log_request(nlh, msg);
+}
+
+int nfnl_log_build_pf_unbind(uint8_t pf, struct nl_msg **result)
+{
+	return build_log_cmd_request(pf, 0, NFULNL_CFG_CMD_PF_UNBIND, result);
+}
+
+int nfnl_log_pf_unbind(struct nl_sock *nlh, uint8_t pf)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_log_build_pf_unbind(pf, &msg)) < 0)
+		return err;
+
+	return send_log_request(nlh, msg);
+}
+
+static int nfnl_log_build_request(const struct nfnl_log *log,
+				  struct nl_msg **result)
 {
 	struct nl_msg *msg;
 
-	msg = nfnl_log_build_bind(queuenum);
-	if (!msg)
-		return nl_get_errno();
-
-	return send_log_msg(nlh, msg);
-}
-
-struct nl_msg *nfnl_log_build_unbind(uint16_t queuenum)
-{
-	return build_log_cmd_msg(0, queuenum, NFULNL_CFG_CMD_UNBIND);
-}
-
-int nfnl_log_unbind(struct nl_handle *nlh, uint16_t queuenum)
-{
-	struct nl_msg *msg;
-
-	msg = nfnl_log_build_bind(queuenum);
-	if (!msg)
-		return nl_get_errno();
-
-	return send_log_msg(nlh, msg);
-}
-
-struct nl_msg *nfnl_log_build_pf_bind(uint8_t pf)
-{
-	return build_log_cmd_msg(pf, 0, NFULNL_CFG_CMD_PF_BIND);
-}
-
-int nfnl_log_pf_bind(struct nl_handle *nlh, uint8_t pf)
-{
-	struct nl_msg *msg;
-
-	msg = nfnl_log_build_pf_bind(pf);
-	if (!msg)
-		return nl_get_errno();
-
-	return send_log_msg(nlh, msg);
-}
-
-struct nl_msg *nfnl_log_build_pf_unbind(uint8_t pf)
-{
-	return build_log_cmd_msg(pf, 0, NFULNL_CFG_CMD_PF_UNBIND);
-}
-
-int nfnl_log_pf_unbind(struct nl_handle *nlh, uint8_t pf)
-{
-	struct nl_msg *msg;
-
-	msg = nfnl_log_build_pf_unbind(pf);
-	if (!msg)
-		return nl_get_errno();
-
-	return send_log_msg(nlh, msg);
-}
-
-struct nl_msg *nfnl_log_build_mode(uint16_t queuenum, uint8_t copy_mode,
-				   uint32_t copy_range)
-{
-	struct nl_msg *msg;
-	struct nfulnl_msg_config_mode mode;
+	if (!nfnl_log_test_group(log))
+		return -NLE_MISSING_ATTR;
 
 	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0,
-			0, queuenum);
+				   0, nfnl_log_get_group(log));
 	if (msg == NULL)
-		return NULL;
+		return -NLE_NOMEM;
 
-	mode.copy_mode = copy_mode;
-	mode.copy_range = htonl(copy_range);
-	if (nla_put(msg, NFULA_CFG_MODE, sizeof(mode), &mode) < 0)
+	/* This sucks. The nfnetlink_log interface always expects both
+	 * parameters to be present. Needs to be done properly.
+	 */
+	if (nfnl_log_test_copy_mode(log)) {
+		struct nfulnl_msg_config_mode mode;
+
+		switch (nfnl_log_get_copy_mode(log)) {
+		case NFNL_LOG_COPY_NONE:
+			mode.copy_mode = NFULNL_COPY_NONE;
+			break;
+		case NFNL_LOG_COPY_META:
+			mode.copy_mode = NFULNL_COPY_META;
+			break;
+		case NFNL_LOG_COPY_PACKET:
+			mode.copy_mode = NFULNL_COPY_PACKET;
+			break;
+		}
+		mode.copy_range = htonl(nfnl_log_get_copy_range(log));
+		mode._pad = 0;
+
+		if (nla_put(msg, NFULA_CFG_MODE, sizeof(mode), &mode) < 0)
+			goto nla_put_failure;
+	}
+
+	if (nfnl_log_test_flush_timeout(log) &&
+	    nla_put_u32(msg, NFULA_CFG_TIMEOUT,
+			htonl(nfnl_log_get_flush_timeout(log))) < 0)
 		goto nla_put_failure;
 
-	return msg;
+	if (nfnl_log_test_alloc_size(log) &&
+	    nla_put_u32(msg, NFULA_CFG_NLBUFSIZ,
+			htonl(nfnl_log_get_alloc_size(log))) < 0)
+		goto nla_put_failure;
+
+	if (nfnl_log_test_queue_threshold(log) &&
+	    nla_put_u32(msg, NFULA_CFG_QTHRESH,
+			htonl(nfnl_log_get_queue_threshold(log))) < 0)
+		goto nla_put_failure;
+
+	*result = msg;
+	return 0;
 
 nla_put_failure:
 	nlmsg_free(msg);
-	return NULL;
+	return -NLE_MSGSIZE;
 }
 
-int nfnl_log_set_mode(struct nl_handle *nlh, uint16_t queuenum,
-		      uint8_t copy_mode, uint32_t copy_range)
+int nfnl_log_build_create_request(const struct nfnl_log *log,
+				  struct nl_msg **result)
+{
+	struct nfulnl_msg_config_cmd cmd;
+	int err;
+
+	if ((err = nfnl_log_build_request(log, result)) < 0)
+		return err;
+
+	cmd.command = NFULNL_CFG_CMD_BIND;
+
+	if (nla_put(*result, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0)
+		goto nla_put_failure;
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(*result);
+	return -NLE_MSGSIZE;
+}
+
+int nfnl_log_create(struct nl_sock *nlh, const struct nfnl_log *log)
 {
 	struct nl_msg *msg;
+	int err;
 
-	msg = nfnl_log_build_mode(queuenum, copy_mode, copy_range);
-	if (!msg)
-		return nl_get_errno();
-	return send_log_msg(nlh, msg);
+	if ((err = nfnl_log_build_create_request(log, &msg)) < 0)
+		return err;
+
+	return send_log_request(nlh, msg);
+}
+
+int nfnl_log_build_change_request(const struct nfnl_log *log,
+				  struct nl_msg **result)
+{
+	return nfnl_log_build_request(log, result);
+}
+
+int nfnl_log_change(struct nl_sock *nlh, const struct nfnl_log *log)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_log_build_change_request(log, &msg)) < 0)
+		return err;
+
+	return send_log_request(nlh, msg);
+}
+
+int nfnl_log_build_delete_request(const struct nfnl_log *log,
+				  struct nl_msg **result)
+{
+	if (!nfnl_log_test_group(log))
+		return -NLE_MISSING_ATTR;
+
+	return build_log_cmd_request(0, nfnl_log_get_group(log),
+				     NFULNL_CFG_CMD_UNBIND, result);
+}
+
+int nfnl_log_delete(struct nl_sock *nlh, const struct nfnl_log *log)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_log_build_delete_request(log, &msg)) < 0)
+		return err;
+
+	return send_log_request(nlh, msg);
 }
 
 /** @} */
 
-#define NFNLMSG_LOG_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_ULOG, (type))
 static struct nl_cache_ops nfnl_log_ops = {
 	.co_name		= "netfilter/log",
-	.co_hdrsize		= NFNL_HDRLEN,
+	.co_obj_ops		= &log_obj_ops,
 	.co_msgtypes		= {
-		{ NFNLMSG_LOG_TYPE(NFULNL_MSG_PACKET), NL_ACT_NEW, "new" },
 		END_OF_MSGTYPES_LIST,
 	},
-	.co_protocol		= NETLINK_NETFILTER,
-	.co_msg_parser		= log_msg_parser,
-	.co_obj_ops		= &log_obj_ops,
 };
 
 static void __init log_init(void)
diff --git a/lib/netfilter/log_msg.c b/lib/netfilter/log_msg.c
new file mode 100644
index 0000000..cad6ddd
--- /dev/null
+++ b/lib/netfilter/log_msg.c
@@ -0,0 +1,209 @@
+/*
+ * lib/netfilter/log_msg.c	Netfilter Log Message
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ */
+
+/**
+ * @ingroup nfnl
+ * @defgroup log Log
+ * @brief
+ * @{
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nfnetlink_log.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/log_msg.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static uint64_t ntohll(uint64_t x)
+{
+	return x;
+}
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+static uint64_t ntohll(uint64_t x)
+{
+	return __bswap_64(x);
+}
+#endif
+
+static struct nla_policy log_msg_policy[NFULA_MAX+1] = {
+	[NFULA_PACKET_HDR]		= {
+		.minlen = sizeof(struct nfulnl_msg_packet_hdr)
+	},
+	[NFULA_MARK]			= { .type = NLA_U32 },
+	[NFULA_TIMESTAMP]		= {
+		.minlen = sizeof(struct nfulnl_msg_packet_timestamp)
+	},
+	[NFULA_IFINDEX_INDEV]		= { .type = NLA_U32 },
+	[NFULA_IFINDEX_OUTDEV]		= { .type = NLA_U32 },
+	[NFULA_IFINDEX_PHYSINDEV]	= { .type = NLA_U32 },
+	[NFULA_IFINDEX_PHYSOUTDEV]	= { .type = NLA_U32 },
+	[NFULA_HWADDR]			= {
+		.minlen = sizeof(struct nfulnl_msg_packet_hw)
+	},
+	//[NFULA_PAYLOAD]
+	[NFULA_PREFIX]			= { .type = NLA_STRING, },
+	[NFULA_UID]			= { .type = NLA_U32 },
+	[NFULA_GID]			= { .type = NLA_U32 },
+	[NFULA_SEQ]			= { .type = NLA_U32 },
+	[NFULA_SEQ_GLOBAL]		= { .type = NLA_U32 },
+};
+
+int nfnlmsg_log_msg_parse(struct nlmsghdr *nlh, struct nfnl_log_msg **result)
+{
+	struct nfnl_log_msg *msg;
+	struct nlattr *tb[NFULA_MAX+1];
+	struct nlattr *attr;
+	int err;
+
+	msg = nfnl_log_msg_alloc();
+	if (!msg)
+		return -NLE_NOMEM;
+
+	msg->ce_msgtype = nlh->nlmsg_type;
+
+	err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFULA_MAX,
+			  log_msg_policy);
+	if (err < 0)
+		goto errout;
+
+	nfnl_log_msg_set_family(msg, nfnlmsg_family(nlh));
+
+	attr = tb[NFULA_PACKET_HDR];
+	if (attr) {
+		struct nfulnl_msg_packet_hdr *hdr = nla_data(attr);
+
+		if (hdr->hw_protocol)
+			nfnl_log_msg_set_hwproto(msg, hdr->hw_protocol);
+		nfnl_log_msg_set_hook(msg, hdr->hook);
+	}
+
+	attr = tb[NFULA_MARK];
+	if (attr)
+		nfnl_log_msg_set_mark(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_TIMESTAMP];
+	if (attr) {
+		struct nfulnl_msg_packet_timestamp *timestamp = nla_data(attr);
+		struct timeval tv;
+
+		tv.tv_sec = ntohll(timestamp->sec);
+		tv.tv_usec = ntohll(timestamp->usec);
+		nfnl_log_msg_set_timestamp(msg, &tv);
+	}
+
+	attr = tb[NFULA_IFINDEX_INDEV];
+	if (attr)
+		nfnl_log_msg_set_indev(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_IFINDEX_OUTDEV];
+	if (attr)
+		nfnl_log_msg_set_outdev(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_IFINDEX_PHYSINDEV];
+	if (attr)
+		nfnl_log_msg_set_physindev(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_IFINDEX_PHYSOUTDEV];
+	if (attr)
+		nfnl_log_msg_set_physoutdev(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_HWADDR];
+	if (attr) {
+		struct nfulnl_msg_packet_hw *hw = nla_data(attr);
+
+		nfnl_log_msg_set_hwaddr(msg, hw->hw_addr, ntohs(hw->hw_addrlen));
+	}
+
+	attr = tb[NFULA_PAYLOAD];
+	if (attr) {
+		err = nfnl_log_msg_set_payload(msg, nla_data(attr), nla_len(attr));
+		if (err < 0)
+			goto errout;
+	}
+
+	attr = tb[NFULA_PREFIX];
+	if (attr) {
+		err = nfnl_log_msg_set_prefix(msg, nla_data(attr));
+		if (err < 0)
+			goto errout;
+	}
+
+	attr = tb[NFULA_UID];
+	if (attr)
+		nfnl_log_msg_set_uid(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_GID];
+	if (attr)
+		nfnl_log_msg_set_gid(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_SEQ];
+	if (attr)
+		nfnl_log_msg_set_seq(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_SEQ_GLOBAL];
+	if (attr)
+		nfnl_log_msg_set_seq_global(msg, ntohl(nla_get_u32(attr)));
+
+	*result = msg;
+	return 0;
+
+errout:
+	nfnl_log_msg_put(msg);
+	return err;
+}
+
+static int log_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+			  struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+	struct nfnl_log_msg *msg;
+	int err;
+
+	if ((err = nfnlmsg_log_msg_parse(nlh, &msg)) < 0)
+		goto errout;
+
+	err = pp->pp_cb((struct nl_object *) msg, pp);
+errout:
+	nfnl_log_msg_put(msg);
+	return err;
+}
+
+/** @} */
+
+#define NFNLMSG_LOG_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_ULOG, (type))
+static struct nl_cache_ops nfnl_log_msg_ops = {
+	.co_name		= "netfilter/log_msg",
+	.co_hdrsize		= NFNL_HDRLEN,
+	.co_msgtypes		= {
+		{ NFNLMSG_LOG_TYPE(NFULNL_MSG_PACKET), NL_ACT_NEW, "new" },
+		END_OF_MSGTYPES_LIST,
+	},
+	.co_protocol		= NETLINK_NETFILTER,
+	.co_msg_parser		= log_msg_parser,
+	.co_obj_ops		= &log_msg_obj_ops,
+};
+
+static void __init log_msg_init(void)
+{
+	nl_cache_mngt_register(&nfnl_log_msg_ops);
+}
+
+static void __exit log_msg_exit(void)
+{
+	nl_cache_mngt_unregister(&nfnl_log_msg_ops);
+}
+
+/** @} */
diff --git a/lib/netfilter/log_msg_obj.c b/lib/netfilter/log_msg_obj.c
new file mode 100644
index 0000000..d2cde4e
--- /dev/null
+++ b/lib/netfilter/log_msg_obj.c
@@ -0,0 +1,458 @@
+/*
+ * lib/netfilter/log_msg_obj.c	Netfilter Log Object
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#include <netlink-local.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/netfilter.h>
+#include <netlink/netfilter/log_msg.h>
+
+/** @cond SKIP */
+#define LOG_MSG_ATTR_FAMILY		(1UL << 0)
+#define LOG_MSG_ATTR_HWPROTO		(1UL << 1)
+#define LOG_MSG_ATTR_HOOK		(1UL << 2)
+#define LOG_MSG_ATTR_MARK		(1UL << 3)
+#define LOG_MSG_ATTR_TIMESTAMP		(1UL << 4)
+#define LOG_MSG_ATTR_INDEV		(1UL << 5)
+#define LOG_MSG_ATTR_OUTDEV		(1UL << 6)
+#define LOG_MSG_ATTR_PHYSINDEV		(1UL << 7)
+#define LOG_MSG_ATTR_PHYSOUTDEV		(1UL << 8)
+#define LOG_MSG_ATTR_HWADDR		(1UL << 9)
+#define LOG_MSG_ATTR_PAYLOAD		(1UL << 10)
+#define LOG_MSG_ATTR_PREFIX		(1UL << 11)
+#define LOG_MSG_ATTR_UID		(1UL << 12)
+#define LOG_MSG_ATTR_GID		(1UL << 13)
+#define LOG_MSG_ATTR_SEQ		(1UL << 14)
+#define LOG_MSG_ATTR_SEQ_GLOBAL		(1UL << 15)
+/** @endcond */
+
+static void log_msg_free_data(struct nl_object *c)
+{
+	struct nfnl_log_msg *msg = (struct nfnl_log_msg *) c;
+
+	if (msg == NULL)
+		return;
+
+	free(msg->log_msg_payload);
+	free(msg->log_msg_prefix);
+}
+
+static int log_msg_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct nfnl_log_msg *dst = (struct nfnl_log_msg *) _dst;
+	struct nfnl_log_msg *src = (struct nfnl_log_msg *) _src;
+	int err;
+
+	if (src->log_msg_payload) {
+		err = nfnl_log_msg_set_payload(dst, src->log_msg_payload,
+					       src->log_msg_payload_len);
+		if (err < 0)
+			goto errout;
+	}
+
+	if (src->log_msg_prefix) {
+		err = nfnl_log_msg_set_prefix(dst, src->log_msg_prefix);
+		if (err < 0)
+			goto errout;
+	}
+
+	return 0;
+errout:
+	return err;
+}
+
+static void log_msg_dump(struct nl_object *a, struct nl_dump_params *p)
+{
+	struct nfnl_log_msg *msg = (struct nfnl_log_msg *) a;
+	struct nl_cache *link_cache;
+	char buf[64];
+
+	link_cache = nl_cache_mngt_require("route/link");
+
+	nl_new_line(p);
+
+	if (msg->ce_mask & LOG_MSG_ATTR_PREFIX)
+		nl_dump(p, "%s", msg->log_msg_prefix);
+
+	if (msg->ce_mask & LOG_MSG_ATTR_INDEV) {
+		if (link_cache)
+			nl_dump(p, "IN=%s ",
+				rtnl_link_i2name(link_cache,
+						 msg->log_msg_indev,
+						 buf, sizeof(buf)));
+		else
+			nl_dump(p, "IN=%d ", msg->log_msg_indev);
+	}
+
+	if (msg->ce_mask & LOG_MSG_ATTR_PHYSINDEV) {
+		if (link_cache)
+			nl_dump(p, "PHYSIN=%s ",
+				rtnl_link_i2name(link_cache,
+						 msg->log_msg_physindev,
+						 buf, sizeof(buf)));
+		else
+			nl_dump(p, "IN=%d ", msg->log_msg_physindev);
+	}
+
+	if (msg->ce_mask & LOG_MSG_ATTR_OUTDEV) {
+		if (link_cache)
+			nl_dump(p, "OUT=%s ",
+				rtnl_link_i2name(link_cache,
+						 msg->log_msg_outdev,
+						 buf, sizeof(buf)));
+		else
+			nl_dump(p, "OUT=%d ", msg->log_msg_outdev);
+	}
+
+	if (msg->ce_mask & LOG_MSG_ATTR_PHYSOUTDEV) {
+		if (link_cache)
+			nl_dump(p, "PHYSOUT=%s ",
+				rtnl_link_i2name(link_cache,
+						 msg->log_msg_physoutdev,
+						 buf, sizeof(buf)));
+		else
+			nl_dump(p, "PHYSOUT=%d ", msg->log_msg_physoutdev);
+	}
+
+	if (msg->ce_mask & LOG_MSG_ATTR_HWADDR) {
+		int i;
+
+		nl_dump(p, "MAC");
+		for (i = 0; i < msg->log_msg_hwaddr_len; i++)
+			nl_dump(p, "%c%02x", i?':':'=', msg->log_msg_hwaddr[i]);
+		nl_dump(p, " ");
+	}
+
+	/* FIXME: parse the payload to get iptables LOG compatible format */
+
+	if (msg->ce_mask & LOG_MSG_ATTR_FAMILY)
+		nl_dump(p, "FAMILY=%s ",
+			nl_af2str(msg->log_msg_family, buf, sizeof(buf)));
+
+	if (msg->ce_mask & LOG_MSG_ATTR_HWPROTO)
+		nl_dump(p, "HWPROTO=%s ",
+			nl_ether_proto2str(ntohs(msg->log_msg_hwproto),
+					   buf, sizeof(buf)));
+
+	if (msg->ce_mask & LOG_MSG_ATTR_HOOK)
+		nl_dump(p, "HOOK=%s ",
+			nfnl_inet_hook2str(msg->log_msg_hook,
+					   buf, sizeof(buf)));
+
+	if (msg->ce_mask & LOG_MSG_ATTR_MARK)
+		nl_dump(p, "MARK=%u ", msg->log_msg_mark);
+
+	if (msg->ce_mask & LOG_MSG_ATTR_PAYLOAD)
+		nl_dump(p, "PAYLOADLEN=%d ", msg->log_msg_payload_len);
+
+	if (msg->ce_mask & LOG_MSG_ATTR_UID)
+		nl_dump(p, "UID=%u ", msg->log_msg_uid);
+
+	if (msg->ce_mask & LOG_MSG_ATTR_GID)
+		nl_dump(p, "GID=%u ", msg->log_msg_gid);
+
+	if (msg->ce_mask & LOG_MSG_ATTR_SEQ)
+		nl_dump(p, "SEQ=%d ", msg->log_msg_seq);
+
+	if (msg->ce_mask & LOG_MSG_ATTR_SEQ_GLOBAL)
+		nl_dump(p, "SEQGLOBAL=%d ", msg->log_msg_seq_global);
+
+	nl_dump(p, "\n");
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nfnl_log_msg *nfnl_log_msg_alloc(void)
+{
+	return (struct nfnl_log_msg *) nl_object_alloc(&log_msg_obj_ops);
+}
+
+void nfnl_log_msg_get(struct nfnl_log_msg *msg)
+{
+	nl_object_get((struct nl_object *) msg);
+}
+
+void nfnl_log_msg_put(struct nfnl_log_msg *msg)
+{
+	nl_object_put((struct nl_object *) msg);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nfnl_log_msg_set_family(struct nfnl_log_msg *msg, uint8_t family)
+{
+	msg->log_msg_family = family;
+	msg->ce_mask |= LOG_MSG_ATTR_FAMILY;
+}
+
+uint8_t nfnl_log_msg_get_family(const struct nfnl_log_msg *msg)
+{
+	if (msg->ce_mask & LOG_MSG_ATTR_FAMILY)
+		return msg->log_msg_family;
+	else
+		return AF_UNSPEC;
+}
+
+void nfnl_log_msg_set_hwproto(struct nfnl_log_msg *msg, uint16_t hwproto)
+{
+	msg->log_msg_hwproto = hwproto;
+	msg->ce_mask |= LOG_MSG_ATTR_HWPROTO;
+}
+
+int nfnl_log_msg_test_hwproto(const struct nfnl_log_msg *msg)
+{
+	return !!(msg->ce_mask & LOG_MSG_ATTR_HWPROTO);
+}
+
+uint16_t nfnl_log_msg_get_hwproto(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_hwproto;
+}
+
+void nfnl_log_msg_set_hook(struct nfnl_log_msg *msg, uint8_t hook)
+{
+	msg->log_msg_hook = hook;
+	msg->ce_mask |= LOG_MSG_ATTR_HOOK;
+}
+
+int nfnl_log_msg_test_hook(const struct nfnl_log_msg *msg)
+{
+	return !!(msg->ce_mask & LOG_MSG_ATTR_HOOK);
+}
+
+uint8_t nfnl_log_msg_get_hook(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_hook;
+}
+
+void nfnl_log_msg_set_mark(struct nfnl_log_msg *msg, uint32_t mark)
+{
+	msg->log_msg_mark = mark;
+	msg->ce_mask |= LOG_MSG_ATTR_MARK;
+}
+
+int nfnl_log_msg_test_mark(const struct nfnl_log_msg *msg)
+{
+	return !!(msg->ce_mask & LOG_MSG_ATTR_MARK);
+}
+
+uint32_t nfnl_log_msg_get_mark(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_mark;
+}
+
+void nfnl_log_msg_set_timestamp(struct nfnl_log_msg *msg, struct timeval *tv)
+{
+	msg->log_msg_timestamp.tv_sec = tv->tv_sec;
+	msg->log_msg_timestamp.tv_usec = tv->tv_usec;
+	msg->ce_mask |= LOG_MSG_ATTR_TIMESTAMP;
+}
+
+const struct timeval *nfnl_log_msg_get_timestamp(const struct nfnl_log_msg *msg)
+{
+	if (!(msg->ce_mask & LOG_MSG_ATTR_TIMESTAMP))
+		return NULL;
+	return &msg->log_msg_timestamp;
+}
+
+void nfnl_log_msg_set_indev(struct nfnl_log_msg *msg, uint32_t indev)
+{
+	msg->log_msg_indev = indev;
+	msg->ce_mask |= LOG_MSG_ATTR_INDEV;
+}
+
+uint32_t nfnl_log_msg_get_indev(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_indev;
+}
+
+void nfnl_log_msg_set_outdev(struct nfnl_log_msg *msg, uint32_t outdev)
+{
+	msg->log_msg_outdev = outdev;
+	msg->ce_mask |= LOG_MSG_ATTR_OUTDEV;
+}
+
+uint32_t nfnl_log_msg_get_outdev(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_outdev;
+}
+
+void nfnl_log_msg_set_physindev(struct nfnl_log_msg *msg, uint32_t physindev)
+{
+	msg->log_msg_physindev = physindev;
+	msg->ce_mask |= LOG_MSG_ATTR_PHYSINDEV;
+}
+
+uint32_t nfnl_log_msg_get_physindev(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_physindev;
+}
+
+void nfnl_log_msg_set_physoutdev(struct nfnl_log_msg *msg, uint32_t physoutdev)
+{
+	msg->log_msg_physoutdev = physoutdev;
+	msg->ce_mask |= LOG_MSG_ATTR_PHYSOUTDEV;
+}
+
+uint32_t nfnl_log_msg_get_physoutdev(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_physoutdev;
+}
+
+void nfnl_log_msg_set_hwaddr(struct nfnl_log_msg *msg, uint8_t *hwaddr, int len)
+{
+	if (len > sizeof(msg->log_msg_hwaddr))
+		len = sizeof(msg->log_msg_hwaddr);
+	msg->log_msg_hwaddr_len = len;
+	memcpy(msg->log_msg_hwaddr, hwaddr, len);
+	msg->ce_mask |= LOG_MSG_ATTR_HWADDR;
+}
+
+const uint8_t *nfnl_log_msg_get_hwaddr(const struct nfnl_log_msg *msg, int *len)
+{
+	if (!(msg->ce_mask & LOG_MSG_ATTR_HWADDR)) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = msg->log_msg_hwaddr_len;
+	return msg->log_msg_hwaddr;
+}
+
+int nfnl_log_msg_set_payload(struct nfnl_log_msg *msg, uint8_t *payload, int len)
+{
+	free(msg->log_msg_payload);
+	msg->log_msg_payload = malloc(len);
+	if (!msg->log_msg_payload)
+		return -NLE_NOMEM;
+
+	memcpy(msg->log_msg_payload, payload, len);
+	msg->log_msg_payload_len = len;
+	msg->ce_mask |= LOG_MSG_ATTR_PAYLOAD;
+	return 0;
+}
+
+const void *nfnl_log_msg_get_payload(const struct nfnl_log_msg *msg, int *len)
+{
+	if (!(msg->ce_mask & LOG_MSG_ATTR_PAYLOAD)) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = msg->log_msg_payload_len;
+	return msg->log_msg_payload;
+}
+
+int nfnl_log_msg_set_prefix(struct nfnl_log_msg *msg, void *prefix)
+{
+	free(msg->log_msg_prefix);
+	msg->log_msg_prefix = strdup(prefix);
+	if (!msg->log_msg_prefix)
+		return -NLE_NOMEM;
+
+	msg->ce_mask |= LOG_MSG_ATTR_PREFIX;
+	return 0;
+}
+
+const char *nfnl_log_msg_get_prefix(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_prefix;
+}
+
+void nfnl_log_msg_set_uid(struct nfnl_log_msg *msg, uint32_t uid)
+{
+	msg->log_msg_uid = uid;
+	msg->ce_mask |= LOG_MSG_ATTR_UID;
+}
+
+int nfnl_log_msg_test_uid(const struct nfnl_log_msg *msg)
+{
+	return !!(msg->ce_mask & LOG_MSG_ATTR_UID);
+}
+
+uint32_t nfnl_log_msg_get_uid(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_uid;
+}
+
+void nfnl_log_msg_set_gid(struct nfnl_log_msg *msg, uint32_t gid)
+{
+	msg->log_msg_gid = gid;
+	msg->ce_mask |= LOG_MSG_ATTR_GID;
+}
+
+int nfnl_log_msg_test_gid(const struct nfnl_log_msg *msg)
+{
+	return !!(msg->ce_mask & LOG_MSG_ATTR_GID);
+}
+
+uint32_t nfnl_log_msg_get_gid(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_gid;
+}
+
+
+void nfnl_log_msg_set_seq(struct nfnl_log_msg *msg, uint32_t seq)
+{
+	msg->log_msg_seq = seq;
+	msg->ce_mask |= LOG_MSG_ATTR_SEQ;
+}
+
+int nfnl_log_msg_test_seq(const struct nfnl_log_msg *msg)
+{
+	return !!(msg->ce_mask & LOG_MSG_ATTR_SEQ);
+}
+
+uint32_t nfnl_log_msg_get_seq(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_seq;
+}
+
+void nfnl_log_msg_set_seq_global(struct nfnl_log_msg *msg, uint32_t seq_global)
+{
+	msg->log_msg_seq_global = seq_global;
+	msg->ce_mask |= LOG_MSG_ATTR_SEQ_GLOBAL;
+}
+
+int nfnl_log_msg_test_seq_global(const struct nfnl_log_msg *msg)
+{
+	return !!(msg->ce_mask & LOG_MSG_ATTR_SEQ_GLOBAL);
+}
+
+uint32_t nfnl_log_msg_get_seq_global(const struct nfnl_log_msg *msg)
+{
+	return msg->log_msg_seq_global;
+}
+
+/** @} */
+
+struct nl_object_ops log_msg_obj_ops = {
+	.oo_name		= "netfilter/log_msg",
+	.oo_size		= sizeof(struct nfnl_log_msg),
+	.oo_free_data		= log_msg_free_data,
+	.oo_clone		= log_msg_clone,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= log_msg_dump,
+	    [NL_DUMP_DETAILS]	= log_msg_dump,
+	    [NL_DUMP_STATS]	= log_msg_dump,
+	},
+};
+
+/** @} */
diff --git a/lib/netfilter/log_obj.c b/lib/netfilter/log_obj.c
index c3adc51..ff2b63a 100644
--- a/lib/netfilter/log_obj.c
+++ b/lib/netfilter/log_obj.c
@@ -6,9 +6,10 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
  */
 
 #include <netlink-local.h>
@@ -16,143 +17,61 @@
 #include <netlink/netfilter/log.h>
 
 /** @cond SKIP */
-#define LOG_ATTR_FAMILY			(1UL << 0)
-#define LOG_ATTR_HWPROTO		(1UL << 1)
-#define LOG_ATTR_HOOK			(1UL << 2)
-#define LOG_ATTR_MARK			(1UL << 3)
-#define LOG_ATTR_TIMESTAMP		(1UL << 4)
-#define LOG_ATTR_INDEV			(1UL << 5)
-#define LOG_ATTR_OUTDEV			(1UL << 6)
-#define LOG_ATTR_PHYSINDEV		(1UL << 7)
-#define LOG_ATTR_PHYSOUTDEV		(1UL << 8)
-#define LOG_ATTR_HWADDR			(1UL << 9)
-#define LOG_ATTR_PAYLOAD		(1UL << 10)
-#define LOG_ATTR_PREFIX			(1UL << 11)
-#define LOG_ATTR_UID			(1UL << 12)
-#define LOG_ATTR_SEQ			(1UL << 13)
-#define LOG_ATTR_SEQ_GLOBAL		(1UL << 14)
+#define LOG_ATTR_GROUP			(1UL << 0)
+#define LOG_ATTR_COPY_MODE		(1UL << 1)
+#define LOG_ATTR_COPY_RANGE		(1UL << 3)
+#define LOG_ATTR_FLUSH_TIMEOUT		(1UL << 4)
+#define LOG_ATTR_ALLOC_SIZE		(1UL << 5)
+#define LOG_ATTR_QUEUE_THRESHOLD	(1UL << 6)
+
 /** @endcond */
 
-static void log_free_data(struct nl_object *c)
-{
-	struct nfnl_log *log = (struct nfnl_log *) c;
-
-	if (log == NULL)
-		return;
-
-	free(log->log_payload);
-	free(log->log_prefix);
-}
-
-static int log_clone(struct nl_object *_dst, struct nl_object *_src)
-{
-	struct nfnl_log *dst = (struct nfnl_log *) _dst;
-	struct nfnl_log *src = (struct nfnl_log *) _src;
-	int err;
-
-	if (src->log_payload) {
-		err = nfnl_log_set_payload(dst, src->log_payload,
-					   src->log_payload_len);
-		if (err < 0)
-			goto errout;
-	}
-
-	if (src->log_prefix) {
-		err = nfnl_log_set_prefix(dst, src->log_prefix);
-		if (err < 0)
-			goto errout;
-	}
-
-	return 0;
-errout:
-	return err;
-}
-
-static int log_dump(struct nl_object *a, struct nl_dump_params *p)
+static void nfnl_log_dump(struct nl_object *a, struct nl_dump_params *p)
 {
 	struct nfnl_log *log = (struct nfnl_log *) a;
-	struct nl_cache *link_cache;
 	char buf[64];
 
-	link_cache = nl_cache_mngt_require("route/link");
+	nl_new_line(p);
 
-	if (log->ce_mask & LOG_ATTR_PREFIX)
-		dp_dump(p, "%s", log->log_prefix);
+	if (log->ce_mask & LOG_ATTR_GROUP)
+		nl_dump(p, "group=%u ", log->log_group);
 
-	if (log->ce_mask & LOG_ATTR_INDEV) {
-		if (link_cache)
-			dp_dump(p, "IN=%s ",
-				rtnl_link_i2name(link_cache, log->log_indev,
-						 buf, sizeof(buf)));
-		else
-			dp_dump(p, "IN=%d ", log->log_indev);
-	}
+	if (log->ce_mask & LOG_ATTR_COPY_MODE)
+		nl_dump(p, "copy_mode=%s ",
+			nfnl_log_copy_mode2str(log->log_copy_mode,
+					       buf, sizeof(buf)));
 
-	if (log->ce_mask & LOG_ATTR_PHYSINDEV) {
-		if (link_cache)
-			dp_dump(p, "PHYSIN=%s ",
-				rtnl_link_i2name(link_cache, log->log_physindev,
-						 buf, sizeof(buf)));
-		else
-			dp_dump(p, "IN=%d ", log->log_physindev);
-	}
+	if (log->ce_mask & LOG_ATTR_COPY_RANGE)
+		nl_dump(p, "copy_range=%u ", log->log_copy_range);
 
-	if (log->ce_mask & LOG_ATTR_OUTDEV) {
-		if (link_cache)
-			dp_dump(p, "OUT=%s ",
-				rtnl_link_i2name(link_cache, log->log_outdev,
-						 buf, sizeof(buf)));
-		else
-			dp_dump(p, "OUT=%d ", log->log_outdev);
-	}
+	if (log->ce_mask & LOG_ATTR_FLUSH_TIMEOUT)
+		nl_dump(p, "flush_timeout=%u ", log->log_flush_timeout);
 
-	if (log->ce_mask & LOG_ATTR_PHYSOUTDEV) {
-		if (link_cache)
-			dp_dump(p, "PHYSOUT=%s ",
-				rtnl_link_i2name(link_cache,log->log_physoutdev,
-						 buf, sizeof(buf)));
-		else
-			dp_dump(p, "PHYSOUT=%d ", log->log_physoutdev);
-	}
+	if (log->ce_mask & LOG_ATTR_ALLOC_SIZE)
+		nl_dump(p, "alloc_size=%u ", log->log_alloc_size);
 
-	if (log->ce_mask & LOG_ATTR_HWADDR) {
-		int i;
+	if (log->ce_mask & LOG_ATTR_QUEUE_THRESHOLD)
+		nl_dump(p, "queue_threshold=%u ", log->log_queue_threshold);
 
-		dp_dump(p, "MAC");
-		for (i = 0; i < log->log_hwaddr_len; i++)
-			dp_dump(p, "%c%02x", i?':':'=', log->log_hwaddr[i]);
-		dp_dump(p, " ");
-	}
+	nl_dump(p, "\n");
+}
 
-	/* FIXME: parse the payload to get iptables LOG compatible format */
+static struct trans_tbl copy_modes[] = {
+	__ADD(NFNL_LOG_COPY_NONE,	none)
+	__ADD(NFNL_LOG_COPY_META,	meta)
+	__ADD(NFNL_LOG_COPY_PACKET,	packet)
+};
 
-	if (log->ce_mask & LOG_ATTR_FAMILY)
-		dp_dump(p, "FAMILY=%s ",
-			nl_af2str(log->log_family, buf, sizeof(buf)));
+char *nfnl_log_copy_mode2str(enum nfnl_log_copy_mode copy_mode, char *buf,
+			     size_t len)
+{
+	return __type2str(copy_mode, buf, len, copy_modes,
+			  ARRAY_SIZE(copy_modes));
+}
 
-	if (log->ce_mask & LOG_ATTR_HWPROTO)
-		dp_dump(p, "HWPROTO=%s ",
-			nl_ether_proto2str(ntohs(log->log_hwproto),
-					   buf, sizeof(buf)));
-
-	if (log->ce_mask & LOG_ATTR_HOOK)
-		dp_dump(p, "HOOK=%d ", log->log_hook);
-
-	if (log->ce_mask & LOG_ATTR_MARK)
-		dp_dump(p, "MARK=%d ", log->log_mark);
-
-	if (log->ce_mask & LOG_ATTR_PAYLOAD)
-		dp_dump(p, "PAYLOADLEN=%d ", log->log_payload_len);
-
-	if (log->ce_mask & LOG_ATTR_SEQ)
-		dp_dump(p, "SEQ=%d ", log->log_seq);
-
-	if (log->ce_mask & LOG_ATTR_SEQ_GLOBAL)
-		dp_dump(p, "SEQGLOBAL=%d ", log->log_seq_global);
-
-	dp_dump(p, "\n");
-
-	return 1;
+enum nfnl_log_copy_mode nfnl_log_str2copy_mode(const char *name)
+{
+	return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes));
 }
 
 /**
@@ -182,232 +101,172 @@
  * @{
  */
 
-void nfnl_log_set_family(struct nfnl_log *log, uint8_t family)
+void nfnl_log_set_group(struct nfnl_log *log, uint16_t group)
 {
-	log->log_family = family;
-	log->ce_mask |= LOG_ATTR_FAMILY;
+	log->log_group = group;
+	log->ce_mask |= LOG_ATTR_GROUP;
 }
 
-uint8_t nfnl_log_get_family(const struct nfnl_log *log)
+int nfnl_log_test_group(const struct nfnl_log *log)
 {
-	if (log->ce_mask & LOG_ATTR_FAMILY)
-		return log->log_family;
-	else
-		return AF_UNSPEC;
+	return !!(log->ce_mask & LOG_ATTR_GROUP);
 }
 
-void nfnl_log_set_hwproto(struct nfnl_log *log, uint16_t hwproto)
+uint16_t nfnl_log_get_group(const struct nfnl_log *log)
 {
-	log->log_hwproto = hwproto;
-	log->ce_mask |= LOG_ATTR_HWPROTO;
+	return log->log_group;
 }
 
-int nfnl_log_test_hwproto(const struct nfnl_log *log)
+void nfnl_log_set_copy_mode(struct nfnl_log *log, enum nfnl_log_copy_mode mode)
 {
-	return !!(log->ce_mask & LOG_ATTR_HWPROTO);
+	log->log_copy_mode = mode;
+	log->ce_mask |= LOG_ATTR_COPY_MODE;
 }
 
-uint16_t nfnl_log_get_hwproto(const struct nfnl_log *log)
+int nfnl_log_test_copy_mode(const struct nfnl_log *log)
 {
-	return log->log_hwproto;
+	return !!(log->ce_mask & LOG_ATTR_COPY_MODE);
 }
 
-void nfnl_log_set_hook(struct nfnl_log *log, uint8_t hook)
+enum nfnl_log_copy_mode nfnl_log_get_copy_mode(const struct nfnl_log *log)
 {
-	log->log_hook = hook;
-	log->ce_mask |= LOG_ATTR_HOOK;
+	return log->log_copy_mode;
 }
 
-int nfnl_log_test_hook(const struct nfnl_log *log)
+void nfnl_log_set_copy_range(struct nfnl_log *log, uint32_t copy_range)
 {
-	return !!(log->ce_mask & LOG_ATTR_HOOK);
+	log->log_copy_range = copy_range;
+	log->ce_mask |= LOG_ATTR_COPY_RANGE;
 }
 
-uint8_t nfnl_log_get_hook(const struct nfnl_log *log)
+int nfnl_log_test_copy_range(const struct nfnl_log *log)
 {
-	return log->log_hook;
+	return !!(log->ce_mask & LOG_ATTR_COPY_RANGE);
 }
 
-void nfnl_log_set_mark(struct nfnl_log *log, uint32_t mark)
+uint32_t nfnl_log_get_copy_range(const struct nfnl_log *log)
 {
-	log->log_mark = mark;
-	log->ce_mask |= LOG_ATTR_MARK;
+	return log->log_copy_range;
 }
 
-int nfnl_log_test_mark(const struct nfnl_log *log)
+void nfnl_log_set_flush_timeout(struct nfnl_log *log, uint32_t timeout)
 {
-	return !!(log->ce_mask & LOG_ATTR_MARK);
+	log->log_flush_timeout = timeout;
+	log->ce_mask |= LOG_ATTR_FLUSH_TIMEOUT;
 }
 
-uint32_t nfnl_log_get_mark(const struct nfnl_log *log)
+int nfnl_log_test_flush_timeout(const struct nfnl_log *log)
 {
-	return log->log_mark;
+	return !!(log->ce_mask & LOG_ATTR_FLUSH_TIMEOUT);
 }
 
-void nfnl_log_set_timestamp(struct nfnl_log *log, struct timeval *tv)
+uint32_t nfnl_log_get_flush_timeout(const struct nfnl_log *log)
 {
-	log->log_timestamp.tv_sec = tv->tv_sec;
-	log->log_timestamp.tv_usec = tv->tv_usec;
-	log->ce_mask |= LOG_ATTR_TIMESTAMP;
+	return log->log_flush_timeout;
 }
 
-const struct timeval *nfnl_log_get_timestamp(const struct nfnl_log *log)
+void nfnl_log_set_alloc_size(struct nfnl_log *log, uint32_t alloc_size)
 {
-	if (!(log->ce_mask & LOG_ATTR_TIMESTAMP))
-		return NULL;
-	return &log->log_timestamp;
+	log->log_alloc_size = alloc_size;
+	log->ce_mask |= LOG_ATTR_ALLOC_SIZE;
 }
 
-void nfnl_log_set_indev(struct nfnl_log *log, uint32_t indev)
+int nfnl_log_test_alloc_size(const struct nfnl_log *log)
 {
-	log->log_indev = indev;
-	log->ce_mask |= LOG_ATTR_INDEV;
+	return !!(log->ce_mask & LOG_ATTR_ALLOC_SIZE);
 }
 
-uint32_t nfnl_log_get_indev(const struct nfnl_log *log)
+uint32_t nfnl_log_get_alloc_size(const struct nfnl_log *log)
 {
-	return log->log_indev;
+	return log->log_alloc_size;
 }
 
-void nfnl_log_set_outdev(struct nfnl_log *log, uint32_t outdev)
+void nfnl_log_set_queue_threshold(struct nfnl_log *log, uint32_t threshold)
 {
-	log->log_outdev = outdev;
-	log->ce_mask |= LOG_ATTR_OUTDEV;
+	log->log_queue_threshold = threshold;
+	log->ce_mask |= LOG_ATTR_QUEUE_THRESHOLD;
 }
 
-uint32_t nfnl_log_get_outdev(const struct nfnl_log *log)
+int nfnl_log_test_queue_threshold(const struct nfnl_log *log)
 {
-	return log->log_outdev;
+	return !!(log->ce_mask & LOG_ATTR_QUEUE_THRESHOLD);
 }
 
-void nfnl_log_set_physindev(struct nfnl_log *log, uint32_t physindev)
+uint32_t nfnl_log_get_queue_threshold(const struct nfnl_log *log)
 {
-	log->log_physindev = physindev;
-	log->ce_mask |= LOG_ATTR_PHYSINDEV;
+	return log->log_queue_threshold;
 }
 
-uint32_t nfnl_log_get_physindev(const struct nfnl_log *log)
+/* We don't actually use the flags for anything yet since the
+ * nfnetlog_log interface truly sucks - it only contains the
+ * flag value, but not mask, so we would have to make assumptions
+ * about the supported flags.
+ */
+void nfnl_log_set_flags(struct nfnl_log *log, unsigned int flags)
 {
-	return log->log_physindev;
+	log->log_flags |= flags;
+	log->log_flag_mask |= flags;
 }
 
-void nfnl_log_set_physoutdev(struct nfnl_log *log, uint32_t physoutdev)
+void nfnl_log_unset_flags(struct nfnl_log *log, unsigned int flags)
 {
-	log->log_physoutdev = physoutdev;
-	log->ce_mask |= LOG_ATTR_PHYSOUTDEV;
+	log->log_flags &= ~flags;
+	log->log_flag_mask |= flags;
 }
 
-uint32_t nfnl_log_get_physoutdev(const struct nfnl_log *log)
+static struct trans_tbl log_flags[] = {
+	__ADD(NFNL_LOG_FLAG_SEQ,	seq)
+	__ADD(NFNL_LOG_FLAG_SEQ_GLOBAL,	seq_global)
+};
+
+char *nfnl_log_flags2str(unsigned int flags, char *buf, size_t len)
 {
-	return log->log_physoutdev;
+	return __flags2str(flags, buf, len, log_flags, ARRAY_SIZE(log_flags));
 }
 
-void nfnl_log_set_hwaddr(struct nfnl_log *log, uint8_t *hwaddr, int len)
+unsigned int nfnl_log_str2flags(const char *name)
 {
-	if (len > sizeof(log->log_hwaddr))
-		len = sizeof(log->log_hwaddr);
-	log->log_hwaddr_len = len;
-	memcpy(log->log_hwaddr, hwaddr, len);
-	log->ce_mask |= LOG_ATTR_HWADDR;
+	return __str2flags(name, log_flags, ARRAY_SIZE(log_flags));
 }
 
-const uint8_t *nfnl_log_get_hwaddr(const struct nfnl_log *log, int *len)
+static int nfnl_log_compare(struct nl_object *_a, struct nl_object *_b,
+			    uint32_t attrs, int flags)
 {
-	if (!(log->ce_mask & LOG_ATTR_HWADDR)) {
-		*len = 0;
-		return NULL;
-	}
+	struct nfnl_log *a = (struct nfnl_log *) _a;
+	struct nfnl_log *b = (struct nfnl_log *) _b;
+	int diff = 0;
 
-	*len = log->log_hwaddr_len;
-	return log->log_hwaddr;
+#define NFNL_LOG_DIFF(ATTR, EXPR) \
+	ATTR_DIFF(attrs, LOG_ATTR_##ATTR, a, b, EXPR)
+#define NFNL_LOG_DIFF_VAL(ATTR, FIELD) \
+	NFNL_LOG_DIFF(ATTR, a->FIELD != b->FIELD)
+
+	diff |= NFNL_LOG_DIFF_VAL(GROUP,		log_group);
+	diff |= NFNL_LOG_DIFF_VAL(COPY_MODE,		log_copy_mode);
+	diff |= NFNL_LOG_DIFF_VAL(COPY_RANGE,		log_copy_range);
+	diff |= NFNL_LOG_DIFF_VAL(FLUSH_TIMEOUT,	log_flush_timeout);
+	diff |= NFNL_LOG_DIFF_VAL(ALLOC_SIZE,		log_alloc_size);
+	diff |= NFNL_LOG_DIFF_VAL(QUEUE_THRESHOLD,	log_queue_threshold);
+
+#undef NFNL_LOG_DIFF
+#undef NFNL_LOG_DIFF_VAL
+
+	return diff;
 }
 
-int nfnl_log_set_payload(struct nfnl_log *log, uint8_t *payload, int len)
+static struct trans_tbl nfnl_log_attrs[] = {
+	__ADD(LOG_ATTR_GROUP,		group)
+	__ADD(LOG_ATTR_COPY_MODE,	copy_mode)
+	__ADD(LOG_ATTR_COPY_RANGE,	copy_range)
+	__ADD(LOG_ATTR_FLUSH_TIMEOUT,	flush_timeout)
+	__ADD(LOG_ATTR_ALLOC_SIZE,	alloc_size)
+	__ADD(LOG_ATTR_QUEUE_THRESHOLD, queue_threshold)
+};
+
+static char *nfnl_log_attrs2str(int attrs, char *buf, size_t len)
 {
-	free(log->log_payload);
-	log->log_payload = malloc(len);
-	if (!log->log_payload)
-		return nl_errno(ENOMEM);
-
-	memcpy(log->log_payload, payload, len);
-	log->log_payload_len = len;
-	log->ce_mask |= LOG_ATTR_PAYLOAD;
-	return 0;
-}
-
-const void *nfnl_log_get_payload(const struct nfnl_log *log, int *len)
-{
-	if (!(log->ce_mask & LOG_ATTR_PAYLOAD)) {
-		*len = 0;
-		return NULL;
-	}
-
-	*len = log->log_payload_len;
-	return log->log_payload;
-}
-
-int nfnl_log_set_prefix(struct nfnl_log *log, void *prefix)
-{
-	free(log->log_prefix);
-	log->log_prefix = strdup(prefix);
-	if (!log->log_prefix)
-		return nl_errno(ENOMEM);
-
-	log->ce_mask |= LOG_ATTR_PREFIX;
-	return 0;
-}
-
-const char *nfnl_log_get_prefix(const struct nfnl_log *log)
-{
-	return log->log_prefix;
-}
-
-void nfnl_log_set_uid(struct nfnl_log *log, uint32_t uid)
-{
-	log->log_uid = uid;
-	log->ce_mask |= LOG_ATTR_UID;
-}
-
-int nfnl_log_test_uid(const struct nfnl_log *log)
-{
-	return !!(log->ce_mask & LOG_ATTR_UID);
-}
-
-uint32_t nfnl_log_get_uid(const struct nfnl_log *log)
-{
-	return log->log_uid;
-}
-
-void nfnl_log_set_seq(struct nfnl_log *log, uint32_t seq)
-{
-	log->log_seq = seq;
-	log->ce_mask |= LOG_ATTR_SEQ;
-}
-
-int nfnl_log_test_seq(const struct nfnl_log *log)
-{
-	return !!(log->ce_mask & LOG_ATTR_SEQ);
-}
-
-uint32_t nfnl_log_get_seq(const struct nfnl_log *log)
-{
-	return log->log_seq;
-}
-
-void nfnl_log_set_seq_global(struct nfnl_log *log, uint32_t seq_global)
-{
-	log->log_seq_global = seq_global;
-	log->ce_mask |= LOG_ATTR_SEQ_GLOBAL;
-}
-
-int nfnl_log_test_seq_global(const struct nfnl_log *log)
-{
-	return !!(log->ce_mask & LOG_ATTR_SEQ_GLOBAL);
-}
-
-uint32_t nfnl_log_get_seq_global(const struct nfnl_log *log)
-{
-	return log->log_seq_global;
+	return __flags2str(attrs, buf, len, nfnl_log_attrs,
+			   ARRAY_SIZE(nfnl_log_attrs));
 }
 
 /** @} */
@@ -415,11 +274,14 @@
 struct nl_object_ops log_obj_ops = {
 	.oo_name		= "netfilter/log",
 	.oo_size		= sizeof(struct nfnl_log),
-	.oo_free_data		= log_free_data,
-	.oo_clone		= log_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= log_dump,
-	.oo_dump[NL_DUMP_FULL]	= log_dump,
-	.oo_dump[NL_DUMP_STATS]	= log_dump,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= nfnl_log_dump,
+	    [NL_DUMP_DETAILS]	= nfnl_log_dump,
+	    [NL_DUMP_STATS]	= nfnl_log_dump,
+	},
+	.oo_compare		= nfnl_log_compare,
+	.oo_attrs2str		= nfnl_log_attrs2str,
+	.oo_id_attrs		= LOG_ATTR_GROUP,
 };
 
 /** @} */
diff --git a/lib/netfilter/netfilter.c b/lib/netfilter/netfilter.c
new file mode 100644
index 0000000..f88b355
--- /dev/null
+++ b/lib/netfilter/netfilter.c
@@ -0,0 +1,53 @@
+/*
+ * lib/netfilter/netfilter.c    Netfilter Generic Functions
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <netlink-local.h>
+#include <netlink/netfilter/netfilter.h>
+#include <linux/netfilter.h>
+
+static struct trans_tbl nfnl_verdicts[] = {
+	__ADD(NF_DROP,		NF_DROP)
+	__ADD(NF_ACCEPT,	NF_ACCEPT)
+	__ADD(NF_STOLEN,	NF_STOLEN)
+	__ADD(NF_QUEUE,		NF_QUEUE)
+	__ADD(NF_REPEAT,	NF_REPEAT)
+	__ADD(NF_STOP,		NF_STOP)
+};
+
+char *nfnl_verdict2str(unsigned int verdict, char *buf, size_t len)
+{
+	return __type2str(verdict, buf, len, nfnl_verdicts,
+			  ARRAY_SIZE(nfnl_verdicts));
+}
+
+unsigned int nfnl_str2verdict(const char *name)
+{
+	return __str2type(name, nfnl_verdicts, ARRAY_SIZE(nfnl_verdicts));
+}
+
+static struct trans_tbl nfnl_inet_hooks[] = {
+	__ADD(NF_INET_PRE_ROUTING,	NF_INET_PREROUTING)
+	__ADD(NF_INET_LOCAL_IN,		NF_INET_LOCAL_IN)
+	__ADD(NF_INET_FORWARD,		NF_INET_FORWARD)
+	__ADD(NF_INET_LOCAL_OUT,	NF_INET_LOCAL_OUT)
+	__ADD(NF_INET_POST_ROUTING,	NF_INET_POST_ROUTING)
+};
+
+char *nfnl_inet_hook2str(unsigned int hook, char *buf, size_t len)
+{
+	return __type2str(hook, buf, len, nfnl_inet_hooks,
+			  ARRAY_SIZE(nfnl_inet_hooks));
+}
+
+unsigned int nfnl_str2inet_hook(const char *name)
+{
+	return __str2type(name, nfnl_inet_hooks, ARRAY_SIZE(nfnl_inet_hooks));
+}
diff --git a/lib/netfilter/nfnl.c b/lib/netfilter/nfnl.c
index 554e234..ddce4b9 100644
--- a/lib/netfilter/nfnl.c
+++ b/lib/netfilter/nfnl.c
@@ -6,13 +6,12 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
  */
 
 /**
- * @ingroup nlfam
  * @defgroup nfnl Netfilter Netlink
  *
  * @par Message Format
@@ -47,7 +46,7 @@
  * nla_put_u32(msg, 1, 0x10);
  *
  * // Message is ready to be sent.
- * nl_send_auto_complete(nl_handle, msg);
+ * nl_send_auto_complete(sk, msg);
  *
  * // All done? Free the message.
  * nlmsg_free(msg);
@@ -57,7 +56,7 @@
  * @code
  * // For trivial messages not requiring any subsys specific header or
  * // attributes, nfnl_send_simple() may be used to send messages directly.
- * nfnl_send_simple(nl_handle, SUBSYS, TYPE, 0, FAMILY, RES_ID);
+ * nfnl_send_simple(sk, SUBSYS, TYPE, 0, FAMILY, RES_ID);
  * @endcode
  * @{
  */
@@ -73,7 +72,7 @@
 
 /**
  * Create and connect netfilter netlink socket.
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  *
  * Creates a NETLINK_NETFILTER netlink socket, binds the socket and
  * issues a connection attempt.
@@ -82,9 +81,9 @@
  *
  * @return 0 on success or a negative error code.
  */
-int nfnl_connect(struct nl_handle *handle)
+int nfnl_connect(struct nl_sock *sk)
 {
-	return nl_connect(handle, NETLINK_NETFILTER);
+	return nl_connect(sk, NETLINK_NETFILTER);
 }
 
 /** @} */
@@ -96,7 +95,7 @@
 
 /**
  * Send trivial netfilter netlink message
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg subsys_id	nfnetlink subsystem
  * @arg type		nfnetlink message type
  * @arg flags		message flags
@@ -105,7 +104,7 @@
  *
  * @return Newly allocated netlink message or NULL.
  */
-int nfnl_send_simple(struct nl_handle *handle, uint8_t subsys_id, uint8_t type,
+int nfnl_send_simple(struct nl_sock *sk, uint8_t subsys_id, uint8_t type,
 		     int flags, uint8_t family, uint16_t res_id)
 {
 	struct nfgenmsg hdr = {
@@ -114,7 +113,7 @@
 		.res_id = htons(res_id),
 	};
 
-	return nl_send_simple(handle, NFNLMSG_TYPE(subsys_id, type), flags,
+	return nl_send_simple(sk, NFNLMSG_TYPE(subsys_id, type), flags,
 			      &hdr, sizeof(hdr));
 }
 
@@ -178,7 +177,7 @@
 
 	nfg = nlmsg_reserve(msg, sizeof(*nfg), NLMSG_ALIGNTO);
 	if (nfg == NULL)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	nfg->nfgen_family = family;
 	nfg->version = NFNETLINK_V0;
@@ -236,7 +235,7 @@
 
 	nlh = nlmsg_put(msg, pid, seq, NFNLMSG_TYPE(subsys_id, type), 0, flags);
 	if (nlh == NULL)
-		return nl_get_errno();
+		return -NLE_MSGSIZE;
 
 	return nfnlmsg_append(msg, family, res_id);
 }
diff --git a/lib/netfilter/queue.c b/lib/netfilter/queue.c
new file mode 100644
index 0000000..ff1de0e
--- /dev/null
+++ b/lib/netfilter/queue.c
@@ -0,0 +1,251 @@
+/*
+ * lib/netfilter/queue.c	Netfilter Queue
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
+ */
+
+/**
+ * @ingroup nfnl
+ * @defgroup queue Queue
+ * @brief
+ * @{
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/queue.h>
+
+struct nl_sock *nfnl_queue_socket_alloc(void)
+{
+	struct nl_sock *nlsk;
+
+	nlsk = nl_socket_alloc();
+	if (nlsk)
+		nl_socket_disable_auto_ack(nlsk);
+	return nlsk;
+}
+
+static int send_queue_request(struct nl_sock *sk, struct nl_msg *msg)
+{
+	int err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(sk);
+}
+
+/**
+ * @name Queue Commands
+ * @{
+ */
+
+static int build_queue_cmd_request(uint8_t family, uint16_t queuenum,
+				   uint8_t command, struct nl_msg **result)
+{
+	struct nl_msg *msg;
+	struct nfqnl_msg_config_cmd cmd;
+
+	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0,
+				   family, queuenum);
+	if (msg == NULL)
+		return -NLE_NOMEM;
+
+	cmd.pf = htons(family);
+	cmd._pad = 0;
+	cmd.command = command;
+	if (nla_put(msg, NFQA_CFG_CMD, sizeof(cmd), &cmd) < 0)
+		goto nla_put_failure;
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+int nfnl_queue_build_pf_bind(uint8_t pf, struct nl_msg **result)
+{
+	return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_BIND, result);
+}
+
+int nfnl_queue_pf_bind(struct nl_sock *nlh, uint8_t pf)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_queue_build_pf_bind(pf, &msg)) < 0)
+		return err;
+
+	return send_queue_request(nlh, msg);
+}
+
+int nfnl_queue_build_pf_unbind(uint8_t pf, struct nl_msg **result)
+{
+	return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_UNBIND, result);
+}
+
+int nfnl_queue_pf_unbind(struct nl_sock *nlh, uint8_t pf)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_queue_build_pf_unbind(pf, &msg)) < 0)
+		return err;
+
+	return send_queue_request(nlh, msg);
+}
+
+static int nfnl_queue_build_request(const struct nfnl_queue *queue,
+				    struct nl_msg **result)
+{
+	struct nl_msg *msg;
+
+	if (!nfnl_queue_test_group(queue))
+		return -NLE_MISSING_ATTR;
+
+	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0,
+				   0, nfnl_queue_get_group(queue));
+	if (msg == NULL)
+		return -NLE_NOMEM;
+
+	if (nfnl_queue_test_maxlen(queue) &&
+	    nla_put_u32(msg, NFQA_CFG_QUEUE_MAXLEN,
+			htonl(nfnl_queue_get_maxlen(queue))) < 0)
+		goto nla_put_failure;
+
+	/* This sucks, the nfnetlink_queue interface always expects both
+	 * parameters to be present. Needs to be done properly.
+	 */
+	if (nfnl_queue_test_copy_mode(queue)) {
+		struct nfqnl_msg_config_params params;
+
+		switch (nfnl_queue_get_copy_mode(queue)) {
+		case NFNL_QUEUE_COPY_NONE:
+			params.copy_mode = NFQNL_COPY_NONE;
+			break;
+		case NFNL_QUEUE_COPY_META:
+			params.copy_mode = NFQNL_COPY_META;
+			break;
+		case NFNL_QUEUE_COPY_PACKET:
+			params.copy_mode = NFQNL_COPY_PACKET;
+			break;
+		}
+		params.copy_range = htonl(nfnl_queue_get_copy_range(queue));
+
+		if (nla_put(msg, NFQA_CFG_PARAMS, sizeof(params), &params) < 0)
+			goto nla_put_failure;
+	}
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+int nfnl_queue_build_create_request(const struct nfnl_queue *queue,
+				    struct nl_msg **result)
+{
+	struct nfqnl_msg_config_cmd cmd;
+	int err;
+
+	if ((err = nfnl_queue_build_request(queue, result)) < 0)
+		return err;
+
+	cmd.pf = 0;
+	cmd._pad = 0;
+	cmd.command = NFQNL_CFG_CMD_BIND;
+
+	NLA_PUT(*result, NFQA_CFG_CMD, sizeof(cmd), &cmd);
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(*result);
+	return -NLE_MSGSIZE;
+}
+
+int nfnl_queue_create(struct nl_sock *nlh, const struct nfnl_queue *queue)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_queue_build_create_request(queue, &msg)) < 0)
+		return err;
+
+	return send_queue_request(nlh, msg);
+}
+
+int nfnl_queue_build_change_request(const struct nfnl_queue *queue,
+				    struct nl_msg **result)
+{
+	return nfnl_queue_build_request(queue, result);
+}
+
+int nfnl_queue_change(struct nl_sock *nlh, const struct nfnl_queue *queue)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_queue_build_change_request(queue, &msg)) < 0)
+		return err;
+
+	return send_queue_request(nlh, msg);
+}
+
+int nfnl_queue_build_delete_request(const struct nfnl_queue *queue,
+				    struct nl_msg **result)
+{
+	if (!nfnl_queue_test_group(queue))
+		return -NLE_MISSING_ATTR;
+
+	return build_queue_cmd_request(0, nfnl_queue_get_group(queue),
+				       NFQNL_CFG_CMD_UNBIND, result);
+}
+
+int nfnl_queue_delete(struct nl_sock *nlh, const struct nfnl_queue *queue)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = nfnl_queue_build_delete_request(queue, &msg)) < 0)
+		return err;
+
+	return send_queue_request(nlh, msg);
+}
+
+/** @} */
+
+static struct nl_cache_ops nfnl_queue_ops = {
+	.co_name		= "netfilter/queue",
+	.co_obj_ops		= &queue_obj_ops,
+	.co_msgtypes		= {
+		END_OF_MSGTYPES_LIST,
+	},
+};
+
+static void __init nfnl_queue_init(void)
+{
+	nl_cache_mngt_register(&nfnl_queue_ops);
+}
+
+static void __exit nfnl_queue_exit(void)
+{
+	nl_cache_mngt_unregister(&nfnl_queue_ops);
+}
+
+/** @} */
diff --git a/lib/netfilter/queue_msg.c b/lib/netfilter/queue_msg.c
new file mode 100644
index 0000000..ab0a58b
--- /dev/null
+++ b/lib/netfilter/queue_msg.c
@@ -0,0 +1,284 @@
+/*
+ * lib/netfilter/queue_msg.c	Netfilter Queue Messages
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2010       Karl Hiramoto <karl@hiramoto.org>
+ */
+
+/**
+ * @ingroup nfnl
+ * @defgroup queue Queue
+ * @brief
+ * @{
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/queue_msg.h>
+
+static struct nl_cache_ops nfnl_queue_msg_ops;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static uint64_t ntohll(uint64_t x)
+{
+	return x;
+}
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+static uint64_t ntohll(uint64_t x)
+{
+	return __bswap_64(x);
+}
+#endif
+
+static struct nla_policy queue_policy[NFQA_MAX+1] = {
+	[NFQA_PACKET_HDR]		= {
+		.minlen	= sizeof(struct nfqnl_msg_packet_hdr),
+	},
+	[NFQA_VERDICT_HDR]		= {
+		.minlen	= sizeof(struct nfqnl_msg_verdict_hdr),
+	},
+	[NFQA_MARK]			= { .type = NLA_U32 },
+	[NFQA_TIMESTAMP]		= {
+		.minlen = sizeof(struct nfqnl_msg_packet_timestamp),
+	},
+	[NFQA_IFINDEX_INDEV]		= { .type = NLA_U32 },
+	[NFQA_IFINDEX_OUTDEV]		= { .type = NLA_U32 },
+	[NFQA_IFINDEX_PHYSINDEV]	= { .type = NLA_U32 },
+	[NFQA_IFINDEX_PHYSOUTDEV]	= { .type = NLA_U32 },
+	[NFQA_HWADDR]			= {
+		.minlen	= sizeof(struct nfqnl_msg_packet_hw),
+	},
+};
+
+int nfnlmsg_queue_msg_parse(struct nlmsghdr *nlh,
+			    struct nfnl_queue_msg **result)
+{
+	struct nfnl_queue_msg *msg;
+	struct nlattr *tb[NFQA_MAX+1];
+	struct nlattr *attr;
+	int err;
+
+	msg = nfnl_queue_msg_alloc();
+	if (!msg)
+		return -NLE_NOMEM;
+
+	msg->ce_msgtype = nlh->nlmsg_type;
+
+	err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFQA_MAX,
+			  queue_policy);
+	if (err < 0)
+		goto errout;
+
+	nfnl_queue_msg_set_group(msg, nfnlmsg_res_id(nlh));
+	nfnl_queue_msg_set_family(msg, nfnlmsg_family(nlh));
+
+	attr = tb[NFQA_PACKET_HDR];
+	if (attr) {
+		struct nfqnl_msg_packet_hdr *hdr = nla_data(attr);
+
+		nfnl_queue_msg_set_packetid(msg, ntohl(hdr->packet_id));
+		if (hdr->hw_protocol)
+			nfnl_queue_msg_set_hwproto(msg, hdr->hw_protocol);
+		nfnl_queue_msg_set_hook(msg, hdr->hook);
+	}
+
+	attr = tb[NFQA_MARK];
+	if (attr)
+		nfnl_queue_msg_set_mark(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFQA_TIMESTAMP];
+	if (attr) {
+		struct nfqnl_msg_packet_timestamp *timestamp = nla_data(attr);
+		struct timeval tv;
+
+		tv.tv_sec = ntohll(timestamp->sec);
+		tv.tv_usec = ntohll(timestamp->usec);
+		nfnl_queue_msg_set_timestamp(msg, &tv);
+	}
+
+	attr = tb[NFQA_IFINDEX_INDEV];
+	if (attr)
+		nfnl_queue_msg_set_indev(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFQA_IFINDEX_OUTDEV];
+	if (attr)
+		nfnl_queue_msg_set_outdev(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFQA_IFINDEX_PHYSINDEV];
+	if (attr)
+		nfnl_queue_msg_set_physindev(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFQA_IFINDEX_PHYSOUTDEV];
+	if (attr)
+		nfnl_queue_msg_set_physoutdev(msg, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFQA_HWADDR];
+	if (attr) {
+		struct nfqnl_msg_packet_hw *hw = nla_data(attr);
+
+		nfnl_queue_msg_set_hwaddr(msg, hw->hw_addr,
+					  ntohs(hw->hw_addrlen));
+	}
+
+	attr = tb[NFQA_PAYLOAD];
+	if (attr) {
+		err = nfnl_queue_msg_set_payload(msg, nla_data(attr),
+						 nla_len(attr));
+		if (err < 0)
+			goto errout;
+	}
+
+	*result = msg;
+	return 0;
+
+errout:
+	nfnl_queue_msg_put(msg);
+	return err;
+}
+
+static int queue_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+			    struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+	struct nfnl_queue_msg *msg;
+	int err;
+
+	if ((err = nfnlmsg_queue_msg_parse(nlh, &msg)) < 0)
+		goto errout;
+
+	err = pp->pp_cb((struct nl_object *) msg, pp);
+errout:
+	nfnl_queue_msg_put(msg);
+	return err;
+}
+
+/** @} */
+
+struct nl_msg *nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg)
+{
+	struct nl_msg *nlmsg;
+	struct nfqnl_msg_verdict_hdr verdict;
+
+	nlmsg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_VERDICT, 0,
+				     nfnl_queue_msg_get_family(msg),
+				     nfnl_queue_msg_get_group(msg));
+	if (nlmsg == NULL)
+		return NULL;
+
+	verdict.id = htonl(nfnl_queue_msg_get_packetid(msg));
+	verdict.verdict = htonl(nfnl_queue_msg_get_verdict(msg));
+	if (nla_put(nlmsg, NFQA_VERDICT_HDR, sizeof(verdict), &verdict) < 0)
+		goto nla_put_failure;
+
+	if (nfnl_queue_msg_test_mark(msg) &&
+	    nla_put_u32(nlmsg, NFQA_MARK,
+			ntohl(nfnl_queue_msg_get_mark(msg))) < 0)
+		goto nla_put_failure;
+
+	return nlmsg;
+
+nla_put_failure:
+	nlmsg_free(nlmsg);
+	return NULL;
+}
+
+/**
+* Send a message verdict/mark
+* @arg nlh            netlink messsage header
+* @arg msg            queue msg
+* @return 0 on OK or error code
+*/
+int nfnl_queue_msg_send_verdict(struct nl_sock *nlh,
+				const struct nfnl_queue_msg *msg)
+{
+	struct nl_msg *nlmsg;
+	int err;
+
+	nlmsg = nfnl_queue_msg_build_verdict(msg);
+	if (nlmsg == NULL)
+		return -NLE_NOMEM;
+
+	err = nl_send_auto_complete(nlh, nlmsg);
+	nlmsg_free(nlmsg);
+	if (err < 0)
+		return err;
+	return wait_for_ack(nlh);
+}
+
+/**
+* Send a message verdict including the payload
+* @arg nlh            netlink messsage header
+* @arg msg            queue msg
+* @arg payload_data   packet payload data
+* @arg payload_len    payload length
+* @return 0 on OK or error code
+*/
+int nfnl_queue_msg_send_verdict_payload(struct nl_sock *nlh,
+				const struct nfnl_queue_msg *msg,
+				const void *payload_data, unsigned payload_len)
+{
+	struct nl_msg *nlmsg;
+	int err;
+	struct iovec iov[3];
+	struct nlattr nla;
+
+	nlmsg = nfnl_queue_msg_build_verdict(msg);
+	if (nlmsg == NULL)
+		return -NLE_NOMEM;
+
+	memset(iov, 0, sizeof(iov));
+
+	iov[0].iov_base = (void *) nlmsg_hdr(nlmsg);
+	iov[0].iov_len = nlmsg_hdr(nlmsg)->nlmsg_len;
+
+	nla.nla_type = NFQA_PAYLOAD;
+	nla.nla_len = payload_len + sizeof(nla);
+	nlmsg_hdr(nlmsg)->nlmsg_len += nla.nla_len;
+
+	iov[1].iov_base = (void *) &nla;
+	iov[1].iov_len = sizeof(nla);
+
+	iov[2].iov_base = (void *) payload_data;
+	iov[2].iov_len = NLA_ALIGN(payload_len);
+
+	nl_auto_complete(nlh, nlmsg);
+	err = nl_send_iovec(nlh, nlmsg, iov, 3);
+
+	nlmsg_free(nlmsg);
+	if (err < 0)
+		return err;
+	return wait_for_ack(nlh);
+}
+
+#define NFNLMSG_QUEUE_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_QUEUE, (type))
+static struct nl_cache_ops nfnl_queue_msg_ops = {
+	.co_name		= "netfilter/queue_msg",
+	.co_hdrsize		= NFNL_HDRLEN,
+	.co_msgtypes		= {
+		{ NFNLMSG_QUEUE_TYPE(NFQNL_MSG_PACKET), NL_ACT_NEW, "new" },
+		END_OF_MSGTYPES_LIST,
+	},
+	.co_protocol		= NETLINK_NETFILTER,
+	.co_msg_parser		= queue_msg_parser,
+	.co_obj_ops		= &queue_msg_obj_ops,
+};
+
+static void __init nfnl_msg_queue_init(void)
+{
+	nl_cache_mngt_register(&nfnl_queue_msg_ops);
+}
+
+static void __exit nfnl_queue_msg_exit(void)
+{
+	nl_cache_mngt_unregister(&nfnl_queue_msg_ops);
+}
+
+/** @} */
diff --git a/lib/netfilter/queue_msg_obj.c b/lib/netfilter/queue_msg_obj.c
new file mode 100644
index 0000000..97813e8
--- /dev/null
+++ b/lib/netfilter/queue_msg_obj.c
@@ -0,0 +1,492 @@
+/*
+ * lib/netfilter/queue_msg_obj.c	Netfilter Queue Message Object
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <netlink-local.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/netfilter.h>
+#include <netlink/netfilter/queue_msg.h>
+#include <linux/netfilter.h>
+
+/** @cond SKIP */
+#define QUEUE_MSG_ATTR_GROUP		(1UL << 0)
+#define QUEUE_MSG_ATTR_FAMILY		(1UL << 1)
+#define QUEUE_MSG_ATTR_PACKETID		(1UL << 2)
+#define QUEUE_MSG_ATTR_HWPROTO		(1UL << 3)
+#define QUEUE_MSG_ATTR_HOOK		(1UL << 4)
+#define QUEUE_MSG_ATTR_MARK		(1UL << 5)
+#define QUEUE_MSG_ATTR_TIMESTAMP	(1UL << 6)
+#define QUEUE_MSG_ATTR_INDEV		(1UL << 7)
+#define QUEUE_MSG_ATTR_OUTDEV		(1UL << 8)
+#define QUEUE_MSG_ATTR_PHYSINDEV	(1UL << 9)
+#define QUEUE_MSG_ATTR_PHYSOUTDEV	(1UL << 10)
+#define QUEUE_MSG_ATTR_HWADDR		(1UL << 11)
+#define QUEUE_MSG_ATTR_PAYLOAD		(1UL << 12)
+#define QUEUE_MSG_ATTR_VERDICT		(1UL << 13)
+/** @endcond */
+
+static void nfnl_queue_msg_free_data(struct nl_object *c)
+{
+	struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) c;
+
+	if (msg == NULL)
+		return;
+
+	free(msg->queue_msg_payload);
+}
+
+static int nfnl_queue_msg_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct nfnl_queue_msg *dst = (struct nfnl_queue_msg *) _dst;
+	struct nfnl_queue_msg *src = (struct nfnl_queue_msg *) _src;
+	int err;
+
+	if (src->queue_msg_payload) {
+		err = nfnl_queue_msg_set_payload(dst, src->queue_msg_payload,
+						 src->queue_msg_payload_len);
+		if (err < 0)
+			goto errout;
+	}
+
+	return 0;
+errout:
+	return err;
+}
+
+static void nfnl_queue_msg_dump(struct nl_object *a, struct nl_dump_params *p)
+{
+	struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) a;
+	struct nl_cache *link_cache;
+	char buf[64];
+
+	link_cache = nl_cache_mngt_require("route/link");
+
+	nl_new_line(p);
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_GROUP)
+		nl_dump(p, "GROUP=%u ", msg->queue_msg_group);
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_INDEV) {
+		if (link_cache)
+			nl_dump(p, "IN=%s ",
+				rtnl_link_i2name(link_cache,
+						 msg->queue_msg_indev,
+						 buf, sizeof(buf)));
+		else
+			nl_dump(p, "IN=%d ", msg->queue_msg_indev);
+	}
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_PHYSINDEV) {
+		if (link_cache)
+			nl_dump(p, "PHYSIN=%s ",
+				rtnl_link_i2name(link_cache,
+						 msg->queue_msg_physindev,
+						 buf, sizeof(buf)));
+		else
+			nl_dump(p, "IN=%d ", msg->queue_msg_physindev);
+	}
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_OUTDEV) {
+		if (link_cache)
+			nl_dump(p, "OUT=%s ",
+				rtnl_link_i2name(link_cache,
+						 msg->queue_msg_outdev,
+						 buf, sizeof(buf)));
+		else
+			nl_dump(p, "OUT=%d ", msg->queue_msg_outdev);
+	}
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_PHYSOUTDEV) {
+		if (link_cache)
+			nl_dump(p, "PHYSOUT=%s ",
+				rtnl_link_i2name(link_cache,
+						 msg->queue_msg_physoutdev,
+						 buf, sizeof(buf)));
+		else
+			nl_dump(p, "PHYSOUT=%d ", msg->queue_msg_physoutdev);
+	}
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_HWADDR) {
+		int i;
+
+		nl_dump(p, "MAC");
+		for (i = 0; i < msg->queue_msg_hwaddr_len; i++)
+			nl_dump(p, "%c%02x", i?':':'=',
+				msg->queue_msg_hwaddr[i]);
+		nl_dump(p, " ");
+	}
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_FAMILY)
+		nl_dump(p, "FAMILY=%s ",
+			nl_af2str(msg->queue_msg_family, buf, sizeof(buf)));
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_HWPROTO)
+		nl_dump(p, "HWPROTO=%s ",
+			nl_ether_proto2str(ntohs(msg->queue_msg_hwproto),
+					   buf, sizeof(buf)));
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_HOOK)
+		nl_dump(p, "HOOK=%s ",
+			nfnl_inet_hook2str(msg->queue_msg_hook,
+					   buf, sizeof(buf)));
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_MARK)
+		nl_dump(p, "MARK=%d ", msg->queue_msg_mark);
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD)
+		nl_dump(p, "PAYLOADLEN=%d ", msg->queue_msg_payload_len);
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_PACKETID)
+		nl_dump(p, "PACKETID=%u ", msg->queue_msg_packetid);
+
+	if (msg->ce_mask & QUEUE_MSG_ATTR_VERDICT)
+		nl_dump(p, "VERDICT=%s ",
+			nfnl_verdict2str(msg->queue_msg_verdict,
+					 buf, sizeof(buf)));
+
+	nl_dump(p, "\n");
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nfnl_queue_msg *nfnl_queue_msg_alloc(void)
+{
+	return (struct nfnl_queue_msg *) nl_object_alloc(&queue_msg_obj_ops);
+}
+
+void nfnl_queue_msg_get(struct nfnl_queue_msg *msg)
+{
+	nl_object_get((struct nl_object *) msg);
+}
+
+void nfnl_queue_msg_put(struct nfnl_queue_msg *msg)
+{
+	nl_object_put((struct nl_object *) msg);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nfnl_queue_msg_set_group(struct nfnl_queue_msg *msg, uint16_t group)
+{
+	msg->queue_msg_group = group;
+	msg->ce_mask |= QUEUE_MSG_ATTR_GROUP;
+}
+
+int nfnl_queue_msg_test_group(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_GROUP);
+}
+
+uint16_t nfnl_queue_msg_get_group(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_group;
+}
+
+/**
+* Set the protocol family
+* @arg msg         NF queue message
+* @arg family      AF_XXX  address family  example: AF_INET, AF_UNIX, etc
+*/
+void nfnl_queue_msg_set_family(struct nfnl_queue_msg *msg, uint8_t family)
+{
+	msg->queue_msg_family = family;
+	msg->ce_mask |= QUEUE_MSG_ATTR_FAMILY;
+}
+
+int nfnl_queue_msg_test_family(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_FAMILY);
+}
+
+uint8_t nfnl_queue_msg_get_family(const struct nfnl_queue_msg *msg)
+{
+	if (msg->ce_mask & QUEUE_MSG_ATTR_FAMILY)
+		return msg->queue_msg_family;
+	else
+		return AF_UNSPEC;
+}
+
+void nfnl_queue_msg_set_packetid(struct nfnl_queue_msg *msg, uint32_t packetid)
+{
+	msg->queue_msg_packetid = packetid;
+	msg->ce_mask |= QUEUE_MSG_ATTR_PACKETID;
+}
+
+int nfnl_queue_msg_test_packetid(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_PACKETID);
+}
+
+uint32_t nfnl_queue_msg_get_packetid(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_packetid;
+}
+
+void nfnl_queue_msg_set_hwproto(struct nfnl_queue_msg *msg, uint16_t hwproto)
+{
+	msg->queue_msg_hwproto = hwproto;
+	msg->ce_mask |= QUEUE_MSG_ATTR_HWPROTO;
+}
+
+int nfnl_queue_msg_test_hwproto(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_HWPROTO);
+}
+
+uint16_t nfnl_queue_msg_get_hwproto(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_hwproto;
+}
+
+void nfnl_queue_msg_set_hook(struct nfnl_queue_msg *msg, uint8_t hook)
+{
+	msg->queue_msg_hook = hook;
+	msg->ce_mask |= QUEUE_MSG_ATTR_HOOK;
+}
+
+int nfnl_queue_msg_test_hook(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_HOOK);
+}
+
+uint8_t nfnl_queue_msg_get_hook(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_hook;
+}
+
+void nfnl_queue_msg_set_mark(struct nfnl_queue_msg *msg, uint32_t mark)
+{
+	msg->queue_msg_mark = mark;
+	msg->ce_mask |= QUEUE_MSG_ATTR_MARK;
+}
+
+int nfnl_queue_msg_test_mark(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_MARK);
+}
+
+uint32_t nfnl_queue_msg_get_mark(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_mark;
+}
+
+void nfnl_queue_msg_set_timestamp(struct nfnl_queue_msg *msg,
+				  struct timeval *tv)
+{
+	msg->queue_msg_timestamp.tv_sec = tv->tv_sec;
+	msg->queue_msg_timestamp.tv_usec = tv->tv_usec;
+	msg->ce_mask |= QUEUE_MSG_ATTR_TIMESTAMP;
+}
+
+int nfnl_queue_msg_test_timestamp(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_TIMESTAMP);
+}
+
+const struct timeval *nfnl_queue_msg_get_timestamp(const struct nfnl_queue_msg *msg)
+{
+	if (!(msg->ce_mask & QUEUE_MSG_ATTR_TIMESTAMP))
+		return NULL;
+	return &msg->queue_msg_timestamp;
+}
+
+void nfnl_queue_msg_set_indev(struct nfnl_queue_msg *msg, uint32_t indev)
+{
+	msg->queue_msg_indev = indev;
+	msg->ce_mask |= QUEUE_MSG_ATTR_INDEV;
+}
+
+int nfnl_queue_msg_test_indev(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_INDEV);
+}
+
+uint32_t nfnl_queue_msg_get_indev(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_indev;
+}
+
+void nfnl_queue_msg_set_outdev(struct nfnl_queue_msg *msg, uint32_t outdev)
+{
+	msg->queue_msg_outdev = outdev;
+	msg->ce_mask |= QUEUE_MSG_ATTR_OUTDEV;
+}
+
+int nfnl_queue_msg_test_outdev(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_OUTDEV);
+}
+
+uint32_t nfnl_queue_msg_get_outdev(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_outdev;
+}
+
+void nfnl_queue_msg_set_physindev(struct nfnl_queue_msg *msg,
+				  uint32_t physindev)
+{
+	msg->queue_msg_physindev = physindev;
+	msg->ce_mask |= QUEUE_MSG_ATTR_PHYSINDEV;
+}
+
+int nfnl_queue_msg_test_physindev(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_PHYSINDEV);
+}
+
+uint32_t nfnl_queue_msg_get_physindev(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_physindev;
+}
+
+void nfnl_queue_msg_set_physoutdev(struct nfnl_queue_msg *msg,
+				   uint32_t physoutdev)
+{
+	msg->queue_msg_physoutdev = physoutdev;
+	msg->ce_mask |= QUEUE_MSG_ATTR_PHYSOUTDEV;
+}
+
+int nfnl_queue_msg_test_physoutdev(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_PHYSOUTDEV);
+}
+
+uint32_t nfnl_queue_msg_get_physoutdev(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_physoutdev;
+}
+
+void nfnl_queue_msg_set_hwaddr(struct nfnl_queue_msg *msg, uint8_t *hwaddr,
+			       int len)
+{
+	if (len > sizeof(msg->queue_msg_hwaddr))
+		len = sizeof(msg->queue_msg_hwaddr);
+
+	msg->queue_msg_hwaddr_len = len;
+	memcpy(msg->queue_msg_hwaddr, hwaddr, len);
+	msg->ce_mask |= QUEUE_MSG_ATTR_HWADDR;
+}
+
+int nfnl_queue_msg_test_hwaddr(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_HWADDR);
+}
+
+const uint8_t *nfnl_queue_msg_get_hwaddr(const struct nfnl_queue_msg *msg,
+					 int *len)
+{
+	if (!(msg->ce_mask & QUEUE_MSG_ATTR_HWADDR)) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = msg->queue_msg_hwaddr_len;
+	return msg->queue_msg_hwaddr;
+}
+
+int nfnl_queue_msg_set_payload(struct nfnl_queue_msg *msg, uint8_t *payload,
+			       int len)
+{
+	free(msg->queue_msg_payload);
+	msg->queue_msg_payload = malloc(len);
+	if (!msg->queue_msg_payload)
+		return -NLE_NOMEM;
+
+	memcpy(msg->queue_msg_payload, payload, len);
+	msg->queue_msg_payload_len = len;
+	msg->ce_mask |= QUEUE_MSG_ATTR_PAYLOAD;
+	return 0;
+}
+
+int nfnl_queue_msg_test_payload(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD);
+}
+
+const void *nfnl_queue_msg_get_payload(const struct nfnl_queue_msg *msg, int *len)
+{
+	if (!(msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD)) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = msg->queue_msg_payload_len;
+	return msg->queue_msg_payload;
+}
+
+/**
+* Return the number of items matching a filter in the cache
+* @arg msg        queue msg
+* @arg verdict    NF_DROP, NF_ACCEPT, NF_REPEAT, etc
+*/
+void nfnl_queue_msg_set_verdict(struct nfnl_queue_msg *msg,
+				unsigned int verdict)
+{
+	msg->queue_msg_verdict = verdict;
+	msg->ce_mask |= QUEUE_MSG_ATTR_VERDICT;
+}
+
+int nfnl_queue_msg_test_verdict(const struct nfnl_queue_msg *msg)
+{
+	return !!(msg->ce_mask & QUEUE_MSG_ATTR_VERDICT);
+}
+
+unsigned int nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *msg)
+{
+	return msg->queue_msg_verdict;
+}
+
+static struct trans_tbl nfnl_queue_msg_attrs[] = {
+	__ADD(QUEUE_MSG_ATTR_GROUP,		group)
+	__ADD(QUEUE_MSG_ATTR_FAMILY,		family)
+	__ADD(QUEUE_MSG_ATTR_PACKETID,		packetid)
+	__ADD(QUEUE_MSG_ATTR_HWPROTO,		hwproto)
+	__ADD(QUEUE_MSG_ATTR_HOOK,		hook)
+	__ADD(QUEUE_MSG_ATTR_MARK,		mark)
+	__ADD(QUEUE_MSG_ATTR_TIMESTAMP,		timestamp)
+	__ADD(QUEUE_MSG_ATTR_INDEV,		indev)
+	__ADD(QUEUE_MSG_ATTR_OUTDEV,		outdev)
+	__ADD(QUEUE_MSG_ATTR_PHYSINDEV,		physindev)
+	__ADD(QUEUE_MSG_ATTR_PHYSOUTDEV,	physoutdev)
+	__ADD(QUEUE_MSG_ATTR_HWADDR,		hwaddr)
+	__ADD(QUEUE_MSG_ATTR_PAYLOAD,		payload)
+	__ADD(QUEUE_MSG_ATTR_VERDICT,		verdict)
+};
+
+static char *nfnl_queue_msg_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nfnl_queue_msg_attrs,
+			   ARRAY_SIZE(nfnl_queue_msg_attrs));
+}
+
+/** @} */
+
+struct nl_object_ops queue_msg_obj_ops = {
+	.oo_name		= "netfilter/queuemsg",
+	.oo_size		= sizeof(struct nfnl_queue_msg),
+	.oo_free_data		= nfnl_queue_msg_free_data,
+	.oo_clone		= nfnl_queue_msg_clone,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= nfnl_queue_msg_dump,
+	    [NL_DUMP_DETAILS]	= nfnl_queue_msg_dump,
+	    [NL_DUMP_STATS]	= nfnl_queue_msg_dump,
+	},
+	.oo_attrs2str		= nfnl_queue_msg_attrs2str,
+};
+
+/** @} */
diff --git a/lib/netfilter/queue_obj.c b/lib/netfilter/queue_obj.c
new file mode 100644
index 0000000..ee03836
--- /dev/null
+++ b/lib/netfilter/queue_obj.c
@@ -0,0 +1,215 @@
+/*
+ * lib/netfilter/queue_obj.c	Netfilter Queue
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
+ */
+
+/**
+ * @ingroup nfnl
+ * @defgroup queue Queue
+ * @brief
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/queue.h>
+
+/** @cond SKIP */
+#define QUEUE_ATTR_GROUP		(1UL << 0)
+#define QUEUE_ATTR_MAXLEN		(1UL << 1)
+#define QUEUE_ATTR_COPY_MODE		(1UL << 2)
+#define QUEUE_ATTR_COPY_RANGE		(1UL << 3)
+/** @endcond */
+
+
+static void nfnl_queue_dump(struct nl_object *a, struct nl_dump_params *p)
+{
+	struct nfnl_queue *queue = (struct nfnl_queue *) a;
+	char buf[64];
+
+	nl_new_line(p);
+
+	if (queue->ce_mask & QUEUE_ATTR_GROUP)
+		nl_dump(p, "group=%u ", queue->queue_group);
+
+	if (queue->ce_mask & QUEUE_ATTR_MAXLEN)
+		nl_dump(p, "maxlen=%u ", queue->queue_maxlen);
+
+	if (queue->ce_mask & QUEUE_ATTR_COPY_MODE)
+		nl_dump(p, "copy_mode=%s ",
+			nfnl_queue_copy_mode2str(queue->queue_copy_mode,
+						 buf, sizeof(buf)));
+
+	if (queue->ce_mask & QUEUE_ATTR_COPY_RANGE)
+		nl_dump(p, "copy_range=%u ", queue->queue_copy_range);
+
+	nl_dump(p, "\n");
+}
+
+static struct trans_tbl copy_modes[] = {
+	__ADD(NFNL_QUEUE_COPY_NONE,	none)
+	__ADD(NFNL_QUEUE_COPY_META,	meta)
+	__ADD(NFNL_QUEUE_COPY_PACKET,	packet)
+};
+
+char *nfnl_queue_copy_mode2str(enum nfnl_queue_copy_mode copy_mode, char *buf,
+			       size_t len)
+{
+	return __type2str(copy_mode, buf, len, copy_modes,
+			   ARRAY_SIZE(copy_modes));
+}
+
+enum nfnl_queue_copy_mode nfnl_queue_str2copy_mode(const char *name)
+{
+	return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes));
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nfnl_queue *nfnl_queue_alloc(void)
+{
+	return (struct nfnl_queue *) nl_object_alloc(&queue_obj_ops);
+}
+
+void nfnl_queue_get(struct nfnl_queue *queue)
+{
+	nl_object_get((struct nl_object *) queue);
+}
+
+void nfnl_queue_put(struct nfnl_queue *queue)
+{
+	nl_object_put((struct nl_object *) queue);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nfnl_queue_set_group(struct nfnl_queue *queue, uint16_t group)
+{
+	queue->queue_group = group;
+	queue->ce_mask |= QUEUE_ATTR_GROUP;
+}
+
+int nfnl_queue_test_group(const struct nfnl_queue *queue)
+{
+	return !!(queue->ce_mask & QUEUE_ATTR_GROUP);
+}
+
+uint16_t nfnl_queue_get_group(const struct nfnl_queue *queue)
+{
+	return queue->queue_group;
+}
+
+void nfnl_queue_set_maxlen(struct nfnl_queue *queue, uint32_t maxlen)
+{
+	queue->queue_maxlen = maxlen;
+	queue->ce_mask |= QUEUE_ATTR_MAXLEN;
+}
+
+int nfnl_queue_test_maxlen(const struct nfnl_queue *queue)
+{
+	return !!(queue->ce_mask & QUEUE_ATTR_MAXLEN);
+}
+
+uint32_t nfnl_queue_get_maxlen(const struct nfnl_queue *queue)
+{
+	return queue->queue_maxlen;
+}
+
+void nfnl_queue_set_copy_mode(struct nfnl_queue *queue, enum nfnl_queue_copy_mode mode)
+{
+	queue->queue_copy_mode = mode;
+	queue->ce_mask |= QUEUE_ATTR_COPY_MODE;
+}
+
+int nfnl_queue_test_copy_mode(const struct nfnl_queue *queue)
+{
+	return !!(queue->ce_mask & QUEUE_ATTR_COPY_MODE);
+}
+
+enum nfnl_queue_copy_mode nfnl_queue_get_copy_mode(const struct nfnl_queue *queue)
+{
+	return queue->queue_copy_mode;
+}
+
+void nfnl_queue_set_copy_range(struct nfnl_queue *queue, uint32_t copy_range)
+{
+	queue->queue_copy_range = copy_range;
+	queue->ce_mask |= QUEUE_ATTR_COPY_RANGE;
+}
+
+int nfnl_queue_test_copy_range(const struct nfnl_queue *queue)
+{
+	return !!(queue->ce_mask & QUEUE_ATTR_COPY_RANGE);
+}
+
+uint32_t nfnl_queue_get_copy_range(const struct nfnl_queue *queue)
+{
+	return queue->queue_copy_range;
+}
+
+static int nfnl_queue_compare(struct nl_object *_a, struct nl_object *_b,
+			      uint32_t attrs, int flags)
+{
+	struct nfnl_queue *a = (struct nfnl_queue *) _a;
+	struct nfnl_queue *b = (struct nfnl_queue *) _b;
+	int diff = 0;
+
+#define NFNL_QUEUE_DIFF(ATTR, EXPR) \
+	ATTR_DIFF(attrs, QUEUE_ATTR_##ATTR, a, b, EXPR)
+#define NFNL_QUEUE_DIFF_VAL(ATTR, FIELD) \
+	NFNL_QUEUE_DIFF(ATTR, a->FIELD != b->FIELD)
+
+	diff |= NFNL_QUEUE_DIFF_VAL(GROUP,	queue_group);
+	diff |= NFNL_QUEUE_DIFF_VAL(MAXLEN,	queue_maxlen);
+	diff |= NFNL_QUEUE_DIFF_VAL(COPY_MODE,	queue_copy_mode);
+	diff |= NFNL_QUEUE_DIFF_VAL(COPY_RANGE,	queue_copy_range);
+
+#undef NFNL_QUEUE_DIFF
+#undef NFNL_QUEUE_DIFF_VAL
+
+	return diff;
+}
+
+static struct trans_tbl nfnl_queue_attrs[] = {
+	__ADD(QUEUE_ATTR_GROUP,		group)
+	__ADD(QUEUE_ATTR_MAXLEN,	maxlen)
+	__ADD(QUEUE_ATTR_COPY_MODE,	copy_mode)
+	__ADD(QUEUE_ATTR_COPY_RANGE,	copy_range)
+};
+
+static char *nfnl_queue_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nfnl_queue_attrs,
+			   ARRAY_SIZE(nfnl_queue_attrs));
+}
+
+/** @} */
+
+struct nl_object_ops queue_obj_ops = {
+	.oo_name		= "netfilter/queue",
+	.oo_size		= sizeof(struct nfnl_queue),
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= nfnl_queue_dump,
+	    [NL_DUMP_DETAILS]	= nfnl_queue_dump,
+	    [NL_DUMP_STATS]	= nfnl_queue_dump,
+	},
+	.oo_compare		= nfnl_queue_compare,
+	.oo_attrs2str		= nfnl_queue_attrs2str,
+	.oo_id_attrs		= QUEUE_ATTR_GROUP,
+};
+
+/** @} */
diff --git a/lib/nl.c b/lib/nl.c
index b425302..c453b60 100644
--- a/lib/nl.c
+++ b/lib/nl.c
@@ -6,102 +6,17 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
- * @defgroup nl Core Netlink API
- * @brief
+ * @defgroup core Core
  *
- * @par Receiving Semantics
- * @code
- *          nl_recvmsgs_default(socket)
- *                 |
- *                 | cb = nl_socket_get_cb(socket)
- *                 v
- *          nl_recvmsgs(socket, cb)
- *                 |           [Application provides nl_recvmsgs() replacement]
- *                 |- - - - - - - - - - - - - - - v
- *                 |                     cb->cb_recvmsgs_ow()
- *                 |
- *                 |               [Application provides nl_recv() replacement]
- * +-------------->|- - - - - - - - - - - - - - - v
- * |           nl_recv()                   cb->cb_recv_ow()
- * |  +----------->|<- - - - - - - - - - - - - - -+
- * |  |            v
- * |  |      Parse Message
- * |  |            |- - - - - - - - - - - - - - - v
- * |  |            |                         NL_CB_MSG_IN()
- * |  |            |<- - - - - - - - - - - - - - -+
- * |  |            |
- * |  |            |- - - - - - - - - - - - - - - v
- * |  |      Sequence Check                NL_CB_SEQ_CHECK()
- * |  |            |<- - - - - - - - - - - - - - -+
- * |  |            |
- * |  |            |- - - - - - - - - - - - - - - v  [ NLM_F_ACK is set ]
- * |  |            |                      NL_CB_SEND_ACK()
- * |  |            |<- - - - - - - - - - - - - - -+
- * |  |            |
- * |  |      +-----+------+--------------+----------------+--------------+
- * |  |      v            v              v                v              v
- * |  | Valid Message    ACK        NOOP Message  End of Multipart  Error Message
- * |  |      |            |              |                |              |
- * |  |      v            v              v                v              v
- * |  |NL_CB_VALID()  NL_CB_ACK()  NL_CB_SKIPPED()  NL_CB_FINISH()  cb->cb_err()
- * |  |      |            |              |                |              |
- * |  |      +------------+--------------+----------------+              v
- * |  |                                  |                           (FAILURE)
- * |  |                                  |  [Callback returned NL_SKIP]
- * |  |  [More messages to be parsed]    |<-----------
- * |  +----------------------------------|
- * |                                     |
- * |         [Multipart message]         |
- * +-------------------------------------|  [Callback returned NL_STOP]
- *                                       |<-----------
- *                                       v
- *                                   (SUCCESS)
- *
- *                          At any time:
- *                                Message Format Error
- *                                         |- - - - - - - - - - - - v
- *                                         v                  NL_CB_INVALID()
- *                                     (FAILURE)
- *
- *                                Message Overrun (Kernel Lost Data)
- *                                         |- - - - - - - - - - - - v
- *                                         v                  NL_CB_OVERRUN()
- *                                     (FAILURE)
- *
- *                                Callback returned negative error code
- *                                     (FAILURE)
- * @endcode
- *
- * @par Sending Semantics
- * @code
- *     nl_send_auto_complete()
- *             |
- *             | Automatically fill in PID and/or sequence number
- *             |
- *             |                   [Application provides nl_send() replacement]
- *             |- - - - - - - - - - - - - - - - - - - - v
- *             v                                 cb->cb_send_ow()
- *         nl_send()
- *             | Add destination address and credentials
- *             v
- *        nl_sendmsg()
- *             | Set source address
- *             |
- *             |- - - - - - - - - - - - - - - - - - - - v
- *             |                                 NL_CB_MSG_OUT()
- *             |<- - - - - - - - - - - - - - - - - - - -+
- *             v
- *         sendmsg()
- * @endcode
- *
+ * @details
  * @par 1) Connecting the socket
  * @code
  * // Bind and connect the socket to a protocol, NETLINK_ROUTE in this example.
- * nl_connect(handle, NETLINK_ROUTE);
+ * nl_connect(sk, NETLINK_ROUTE);
  * @endcode
  *
  * @par 2) Sending data
@@ -110,29 +25,29 @@
  * // a piece of data to the other netlink peer. This method is not
  * // recommended.
  * const char buf[] = { 0x01, 0x02, 0x03, 0x04 };
- * nl_sendto(handle, buf, sizeof(buf));
+ * nl_sendto(sk, buf, sizeof(buf));
  *
  * // A more comfortable interface is nl_send() taking a pointer to
  * // a netlink message.
  * struct nl_msg *msg = my_msg_builder();
- * nl_send(handle, nlmsg_hdr(msg));
+ * nl_send(sk, nlmsg_hdr(msg));
  *
  * // nl_sendmsg() provides additional control over the sendmsg() message
  * // header in order to allow more specific addressing of multiple peers etc.
  * struct msghdr hdr = { ... };
- * nl_sendmsg(handle, nlmsg_hdr(msg), &hdr);
+ * nl_sendmsg(sk, nlmsg_hdr(msg), &hdr);
  *
  * // You're probably too lazy to fill out the netlink pid, sequence number
  * // and message flags all the time. nl_send_auto_complete() automatically
  * // extends your message header as needed with an appropriate sequence
- * // number, the netlink pid stored in the netlink handle and the message
- * // flags NLM_F_REQUEST and NLM_F_ACK
- * nl_send_auto_complete(handle, nlmsg_hdr(msg));
+ * // number, the netlink pid stored in the netlink socket and the message
+ * // flags NLM_F_REQUEST and NLM_F_ACK (if not disabled in the socket)
+ * nl_send_auto_complete(sk, nlmsg_hdr(msg));
  *
  * // Simple protocols don't require the complex message construction interface
  * // and may favour nl_send_simple() to easly send a bunch of payload
  * // encapsulated in a netlink message header.
- * nl_send_simple(handle, MY_MSG_TYPE, 0, buf, sizeof(buf));
+ * nl_send_simple(sk, MY_MSG_TYPE, 0, buf, sizeof(buf));
  * @endcode
  *
  * @par 3) Receiving data
@@ -141,26 +56,26 @@
  * // content and gives back the pointer to you.
  * struct sockaddr_nl peer;
  * unsigned char *msg;
- * nl_recv(handle, &peer, &msg);
+ * nl_recv(sk, &peer, &msg);
  *
  * // nl_recvmsgs() receives a bunch of messages until the callback system
  * // orders it to state, usually after receving a compolete multi part
  * // message series.
- * nl_recvmsgs(handle, my_callback_configuration);
+ * nl_recvmsgs(sk, my_callback_configuration);
  *
  * // nl_recvmsgs_default() acts just like nl_recvmsg() but uses the callback
- * // configuration stored in the handle.
- * nl_recvmsgs_default(handle);
+ * // configuration stored in the socket.
+ * nl_recvmsgs_default(sk);
  *
  * // In case you want to wait for the ACK to be recieved that you requested
  * // with your latest message, you can call nl_wait_for_ack()
- * nl_wait_for_ack(handle);
+ * nl_wait_for_ack(sk);
  * @endcode
  *
  * @par 4) Closing
  * @code
  * // Close the socket first to release kernel memory
- * nl_close(handle);
+ * nl_close(sk);
  * @endcode
  * 
  * @{
@@ -180,7 +95,7 @@
 
 /**
  * Create and connect netlink socket.
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg protocol	Netlink protocol to use.
  *
  * Creates a netlink socket using the specified protocol, binds the socket
@@ -188,70 +103,70 @@
  *
  * @return 0 on success or a negative error code.
  */
-int nl_connect(struct nl_handle *handle, int protocol)
+int nl_connect(struct nl_sock *sk, int protocol)
 {
 	int err;
 	socklen_t addrlen;
 
-	handle->h_fd = socket(AF_NETLINK, SOCK_RAW, protocol);
-	if (handle->h_fd < 0) {
-		err = nl_error(1, "socket(AF_NETLINK, ...) failed");
+	sk->s_fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+	if (sk->s_fd < 0) {
+		err = -nl_syserr2nlerr(errno);
 		goto errout;
 	}
 
-	if (!(handle->h_flags & NL_SOCK_BUFSIZE_SET)) {
-		err = nl_set_buffer_size(handle, 0, 0);
+	if (!(sk->s_flags & NL_SOCK_BUFSIZE_SET)) {
+		err = nl_socket_set_buffer_size(sk, 0, 0);
 		if (err < 0)
 			goto errout;
 	}
 
-	err = bind(handle->h_fd, (struct sockaddr*) &handle->h_local,
-		   sizeof(handle->h_local));
+	err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
+		   sizeof(sk->s_local));
 	if (err < 0) {
-		err = nl_error(1, "bind() failed");
+		err = -nl_syserr2nlerr(errno);
 		goto errout;
 	}
 
-	addrlen = sizeof(handle->h_local);
-	err = getsockname(handle->h_fd, (struct sockaddr *) &handle->h_local,
+	addrlen = sizeof(sk->s_local);
+	err = getsockname(sk->s_fd, (struct sockaddr *) &sk->s_local,
 			  &addrlen);
 	if (err < 0) {
-		err = nl_error(1, "getsockname failed");
+		err = -nl_syserr2nlerr(errno);
 		goto errout;
 	}
 
-	if (addrlen != sizeof(handle->h_local)) {
-		err = nl_error(EADDRNOTAVAIL, "Invalid address length");
+	if (addrlen != sizeof(sk->s_local)) {
+		err = -NLE_NOADDR;
 		goto errout;
 	}
 
-	if (handle->h_local.nl_family != AF_NETLINK) {
-		err = nl_error(EPFNOSUPPORT, "Address format not supported");
+	if (sk->s_local.nl_family != AF_NETLINK) {
+		err = -NLE_AF_NOSUPPORT;
 		goto errout;
 	}
 
-	handle->h_proto = protocol;
+	sk->s_proto = protocol;
 
 	return 0;
 errout:
-	close(handle->h_fd);
-	handle->h_fd = -1;
+	close(sk->s_fd);
+	sk->s_fd = -1;
 
 	return err;
 }
 
 /**
  * Close/Disconnect netlink socket.
- * @arg handle		Netlink handle
+ * @arg sk		Netlink socket.
  */
-void nl_close(struct nl_handle *handle)
+void nl_close(struct nl_sock *sk)
 {
-	if (handle->h_fd >= 0) {
-		close(handle->h_fd);
-		handle->h_fd = -1;
+	if (sk->s_fd >= 0) {
+		close(sk->s_fd);
+		sk->s_fd = -1;
 	}
 
-	handle->h_proto = 0;
+	sk->s_proto = 0;
 }
 
 /** @} */
@@ -263,77 +178,73 @@
 
 /**
  * Send raw data over netlink socket.
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg buf		Data buffer.
  * @arg size		Size of data buffer.
  * @return Number of characters written on success or a negative error code.
  */
-int nl_sendto(struct nl_handle *handle, void *buf, size_t size)
+int nl_sendto(struct nl_sock *sk, void *buf, size_t size)
 {
 	int ret;
 
-	ret = sendto(handle->h_fd, buf, size, 0, (struct sockaddr *)
-		     &handle->h_peer, sizeof(handle->h_peer));
+	ret = sendto(sk->s_fd, buf, size, 0, (struct sockaddr *)
+		     &sk->s_peer, sizeof(sk->s_peer));
 	if (ret < 0)
-		return nl_errno(errno);
+		return -nl_syserr2nlerr(errno);
 
 	return ret;
 }
 
 /**
  * Send netlink message with control over sendmsg() message header.
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg msg		Netlink message to be sent.
  * @arg hdr		Sendmsg() message header.
  * @return Number of characters sent on sucess or a negative error code.
  */
-int nl_sendmsg(struct nl_handle *handle, struct nl_msg *msg, struct msghdr *hdr)
+int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr)
 {
 	struct nl_cb *cb;
 	int ret;
 
-	struct iovec iov = {
-		.iov_base = (void *) nlmsg_hdr(msg),
-		.iov_len = nlmsg_hdr(msg)->nlmsg_len,
-	};
+	nlmsg_set_src(msg, &sk->s_local);
 
-	hdr->msg_iov = &iov;
-	hdr->msg_iovlen = 1;
-
-	nlmsg_set_src(msg, &handle->h_local);
-
-	cb = handle->h_cb;
+	cb = sk->s_cb;
 	if (cb->cb_set[NL_CB_MSG_OUT])
 		if (nl_cb_call(cb, NL_CB_MSG_OUT, msg) != NL_OK)
 			return 0;
 
-	ret = sendmsg(handle->h_fd, hdr, 0);
+	ret = sendmsg(sk->s_fd, hdr, 0);
 	if (ret < 0)
-		return nl_errno(errno);
+		return -nl_syserr2nlerr(errno);
 
+	NL_DBG(4, "sent %d bytes\n", ret);
 	return ret;
 }
 
 
 /**
  * Send netlink message.
- * @arg handle		Netlink handle
+ * @arg sk		Netlink socket.
  * @arg msg		Netlink message to be sent.
+ * @arg iov		iovec to be sent.
+ * @arg iovlen		number of struct iovec to be sent.
  * @see nl_sendmsg()
  * @return Number of characters sent on success or a negative error code.
  */
-int nl_send(struct nl_handle *handle, struct nl_msg *msg)
+int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen)
 {
 	struct sockaddr_nl *dst;
 	struct ucred *creds;
-	
 	struct msghdr hdr = {
-		.msg_name = (void *) &handle->h_peer,
+		.msg_name = (void *) &sk->s_peer,
 		.msg_namelen = sizeof(struct sockaddr_nl),
+		.msg_iov = iov,
+		.msg_iovlen = iovlen,
 	};
 
 	/* Overwrite destination if specified in the message itself, defaults
-	 * to the peer address of the handle.
+	 * to the peer address of the socket.
 	 */
 	dst = nlmsg_get_dst(msg);
 	if (dst->nl_family == AF_NETLINK)
@@ -355,12 +266,51 @@
 		memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred));
 	}
 
-	return nl_sendmsg(handle, msg, &hdr);
+	return nl_sendmsg(sk, msg, &hdr);
+}
+
+
+
+/**
+* Send netlink message.
+* @arg sk		Netlink socket.
+* @arg msg		Netlink message to be sent.
+* @see nl_sendmsg()
+* @return Number of characters sent on success or a negative error code.
+*/
+int nl_send(struct nl_sock *sk, struct nl_msg *msg)
+{
+	struct iovec iov = {
+		.iov_base = (void *) nlmsg_hdr(msg),
+		.iov_len = nlmsg_hdr(msg)->nlmsg_len,
+	};
+
+	return nl_send_iovec(sk, msg, &iov, 1);
+}
+
+void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
+{
+	struct nlmsghdr *nlh;
+
+	nlh = nlmsg_hdr(msg);
+	if (nlh->nlmsg_pid == 0)
+		nlh->nlmsg_pid = sk->s_local.nl_pid;
+
+	if (nlh->nlmsg_seq == 0)
+		nlh->nlmsg_seq = sk->s_seq_next++;
+
+	if (msg->nm_protocol == -1)
+		msg->nm_protocol = sk->s_proto;
+
+	nlh->nlmsg_flags |= NLM_F_REQUEST;
+
+	if (!(sk->s_flags & NL_NO_AUTO_ACK))
+		nlh->nlmsg_flags |= NLM_F_ACK;
 }
 
 /**
  * Send netlink message and check & extend header values as needed.
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg msg		Netlink message to be sent.
  *
  * Checks the netlink message \c nlh for completness and extends it
@@ -370,32 +320,21 @@
  * @see nl_send()
  * @return Number of characters sent or a negative error code.
  */
-int nl_send_auto_complete(struct nl_handle *handle, struct nl_msg *msg)
+int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
 {
-	struct nlmsghdr *nlh;
-	struct nl_cb *cb = handle->h_cb;
+	struct nl_cb *cb = sk->s_cb;
 
-	nlh = nlmsg_hdr(msg);
-	if (nlh->nlmsg_pid == 0)
-		nlh->nlmsg_pid = handle->h_local.nl_pid;
-
-	if (nlh->nlmsg_seq == 0)
-		nlh->nlmsg_seq = handle->h_seq_next++;
-
-	if (msg->nm_protocol == -1)
-		msg->nm_protocol = handle->h_proto;
-	
-	nlh->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK);
+	nl_auto_complete(sk, msg);
 
 	if (cb->cb_send_ow)
-		return cb->cb_send_ow(handle, msg);
+		return cb->cb_send_ow(sk, msg);
 	else
-		return nl_send(handle, msg);
+		return nl_send(sk, msg);
 }
 
 /**
  * Send simple netlink message using nl_send_auto_complete()
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg type		Netlink message type.
  * @arg flags		Netlink message flags.
  * @arg buf		Data buffer.
@@ -407,7 +346,7 @@
  * @see nl_send_auto_complete()
  * @return Number of characters sent on success or a negative error code.
  */
-int nl_send_simple(struct nl_handle *handle, int type, int flags, void *buf,
+int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf,
 		   size_t size)
 {
 	int err;
@@ -415,7 +354,7 @@
 
 	msg = nlmsg_alloc_simple(type, flags);
 	if (!msg)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	if (buf && size) {
 		err = nlmsg_append(msg, buf, size, NLMSG_ALIGNTO);
@@ -424,7 +363,7 @@
 	}
 	
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
 errout:
 	nlmsg_free(msg);
 
@@ -440,7 +379,7 @@
 
 /**
  * Receive data from netlink socket
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg nla		Destination pointer for peer's netlink address.
  * @arg buf		Destination pointer for message content.
  * @arg creds		Destination pointer for credentials.
@@ -457,7 +396,7 @@
  *
  * @return Number of octets read, 0 on EOF or a negative error code.
  */
-int nl_recv(struct nl_handle *handle, struct sockaddr_nl *nla,
+int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla,
 	    unsigned char **buf, struct ucred **creds)
 {
 	int n;
@@ -475,22 +414,22 @@
 	};
 	struct cmsghdr *cmsg;
 
-	if (handle->h_flags & NL_MSG_PEEK)
+	if (sk->s_flags & NL_MSG_PEEK)
 		flags |= MSG_PEEK;
 
 	if (page_size == 0)
 		page_size = getpagesize();
 
 	iov.iov_len = page_size;
-	iov.iov_base = *buf = calloc(1, iov.iov_len);
+	iov.iov_base = *buf = malloc(iov.iov_len);
 
-	if (handle->h_flags & NL_SOCK_PASSCRED) {
+	if (sk->s_flags & NL_SOCK_PASSCRED) {
 		msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
 		msg.msg_control = calloc(1, msg.msg_controllen);
 	}
 retry:
 
-	n = recvmsg(handle->h_fd, &msg, flags);
+	n = recvmsg(sk->s_fd, &msg, flags);
 	if (!n)
 		goto abort;
 	else if (n < 0) {
@@ -503,7 +442,7 @@
 		} else {
 			free(msg.msg_control);
 			free(*buf);
-			return nl_error(errno, "recvmsg failed");
+			return -nl_syserr2nlerr(errno);
 		}
 	}
 
@@ -527,7 +466,7 @@
 	if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
 		free(msg.msg_control);
 		free(*buf);
-		return nl_error(EADDRNOTAVAIL, "socket address size mismatch");
+		return -NLE_NOADDR;
 	}
 
 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
@@ -564,7 +503,7 @@
 	} \
 } while (0)
 
-static int recvmsgs(struct nl_handle *handle, struct nl_cb *cb)
+static int recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
 {
 	int n, err = 0, multipart = 0;
 	unsigned char *buf = NULL;
@@ -574,30 +513,29 @@
 	struct ucred *creds = NULL;
 
 continue_reading:
-	NL_DBG(3, "Attempting to read from %p\n", handle);
+	NL_DBG(3, "Attempting to read from %p\n", sk);
 	if (cb->cb_recv_ow)
-		n = cb->cb_recv_ow(handle, &nla, &buf, &creds);
+		n = cb->cb_recv_ow(sk, &nla, &buf, &creds);
 	else
-		n = nl_recv(handle, &nla, &buf, &creds);
+		n = nl_recv(sk, &nla, &buf, &creds);
 
 	if (n <= 0)
 		return n;
 
-	NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", handle, n);
+	NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", sk, n);
 
 	hdr = (struct nlmsghdr *) buf;
 	while (nlmsg_ok(hdr, n)) {
-		NL_DBG(3, "recgmsgs(%p): Processing valid message...\n",
-		       handle);
+		NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", sk);
 
 		nlmsg_free(msg);
 		msg = nlmsg_convert(hdr);
 		if (!msg) {
-			err = nl_errno(ENOMEM);
+			err = -NLE_NOMEM;
 			goto out;
 		}
 
-		nlmsg_set_proto(msg, handle->h_proto);
+		nlmsg_set_proto(msg, sk->s_proto);
 		nlmsg_set_src(msg, &nla);
 		if (creds)
 			nlmsg_set_creds(msg, creds);
@@ -612,12 +550,11 @@
 		 * enforcing strict ordering */
 		if (cb->cb_set[NL_CB_SEQ_CHECK])
 			NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg);
-		else if (hdr->nlmsg_seq != handle->h_seq_expect) {
+		else if (hdr->nlmsg_seq != sk->s_seq_expect) {
 			if (cb->cb_set[NL_CB_INVALID])
 				NL_CB_CALL(cb, NL_CB_INVALID, msg);
 			else {
-				err = nl_error(EINVAL,
-					"Sequence number mismatch");
+				err = -NLE_SEQ_MISMATCH;
 				goto out;
 			}
 		}
@@ -628,10 +565,10 @@
 		    hdr->nlmsg_type == NLMSG_OVERRUN) {
 			/* We can't check for !NLM_F_MULTI since some netlink
 			 * users in the kernel are broken. */
-			handle->h_seq_expect++;
+			sk->s_seq_expect++;
 			NL_DBG(3, "recvmsgs(%p): Increased expected " \
 			       "sequence number to %d\n",
-			       handle, handle->h_seq_expect);
+			       sk, sk->s_seq_expect);
 		}
 
 		if (hdr->nlmsg_flags & NLM_F_MULTI)
@@ -674,7 +611,7 @@
 			if (cb->cb_set[NL_CB_OVERRUN])
 				NL_CB_CALL(cb, NL_CB_OVERRUN, msg);
 			else {
-				err = nl_error(EOVERFLOW, "Overrun");
+				err = -NLE_MSG_OVERFLOW;
 				goto out;
 			}
 		}
@@ -691,8 +628,7 @@
 				if (cb->cb_set[NL_CB_INVALID])
 					NL_CB_CALL(cb, NL_CB_INVALID, msg);
 				else {
-					err = nl_error(EINVAL,
-					        "Truncated error message");
+					err = -NLE_MSG_TRUNC;
 					goto out;
 				}
 			} else if (e->error) {
@@ -705,13 +641,11 @@
 					else if (err == NL_SKIP)
 						goto skip;
 					else if (err == NL_STOP) {
-						err = nl_error(-e->error,
-						         "Netlink Error");
+						err = -nl_syserr2nlerr(e->error);
 						goto out;
 					}
 				} else {
-					err = nl_error(-e->error,
-						  "Netlink Error");
+					err = -nl_syserr2nlerr(e->error);
 					goto out;
 				}
 			} else if (cb->cb_set[NL_CB_ACK])
@@ -751,7 +685,7 @@
 
 /**
  * Receive a set of messages from a netlink socket.
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg cb		set of callbacks to control behaviour.
  *
  * Repeatedly calls nl_recv() or the respective replacement if provided
@@ -764,23 +698,23 @@
  *
  * @return 0 on success or a negative error code from nl_recv().
  */
-int nl_recvmsgs(struct nl_handle *handle, struct nl_cb *cb)
+int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
 {
 	if (cb->cb_recvmsgs_ow)
-		return cb->cb_recvmsgs_ow(handle, cb);
+		return cb->cb_recvmsgs_ow(sk, cb);
 	else
-		return recvmsgs(handle, cb);
+		return recvmsgs(sk, cb);
 }
 
 /**
- * Receive a set of message from a netlink socket using handlers in nl_handle.
- * @arg handle		netlink handle
+ * Receive a set of message from a netlink socket using handlers in nl_sock.
+ * @arg sk		Netlink socket.
  *
- * Calls nl_recvmsgs() with the handlers configured in the netlink handle.
+ * Calls nl_recvmsgs() with the handlers configured in the netlink socket.
  */
-int nl_recvmsgs_default(struct nl_handle *handle)
+int nl_recvmsgs_default(struct nl_sock *sk)
 {
-	return nl_recvmsgs(handle, handle->h_cb);
+	return nl_recvmsgs(sk, sk->s_cb);
 
 }
 
@@ -791,23 +725,23 @@
 
 /**
  * Wait for ACK.
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @pre The netlink socket must be in blocking state.
  *
  * Waits until an ACK is received for the latest not yet acknowledged
  * netlink message.
  */
-int nl_wait_for_ack(struct nl_handle *handle)
+int nl_wait_for_ack(struct nl_sock *sk)
 {
 	int err;
 	struct nl_cb *cb;
 
-	cb = nl_cb_clone(handle->h_cb);
+	cb = nl_cb_clone(sk->s_cb);
 	if (cb == NULL)
-		return nl_get_errno();
+		return -NLE_NOMEM;
 
 	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL);
-	err = nl_recvmsgs(handle, cb);
+	err = nl_recvmsgs(sk, cb);
 	nl_cb_put(cb);
 
 	return err;
diff --git a/lib/object.c b/lib/object.c
index 74f6e2d..d881ac9 100644
--- a/lib/object.c
+++ b/lib/object.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -47,10 +47,8 @@
 		BUG();
 
 	new = calloc(1, ops->oo_size);
-	if (!new) {
-		nl_errno(ENOMEM);
+	if (!new)
 		return NULL;
-	}
 
 	new->ce_refcnt = 1;
 	nl_init_list_head(&new->ce_list);
@@ -69,17 +67,18 @@
  * @arg kind		name of object type
  * @return The new object or nULL
  */
-struct nl_object *nl_object_alloc_name(const char *kind)
+int nl_object_alloc_name(const char *kind, struct nl_object **result)
 {
 	struct nl_cache_ops *ops;
 
 	ops = nl_cache_ops_lookup(kind);
-	if (!ops) {
-		nl_error(ENOENT, "Unable to lookup cache kind \"%s\"", kind);
-		return NULL;
-	}
+	if (!ops)
+		return -NLE_OPNOTSUPP;
 
-	return nl_object_alloc(ops->co_obj_ops);
+	if (!(*result = nl_object_alloc(ops->co_obj_ops)))
+		return -NLE_NOMEM;
+
+	return 0;
 }
 
 struct nl_derived_object {
@@ -109,6 +108,7 @@
 
 	new->ce_ops = obj->ce_ops;
 	new->ce_msgtype = obj->ce_msgtype;
+	new->ce_mask = obj->ce_mask;
 
 	if (size)
 		memcpy((void *)new + doff, (void *)obj + doff, size);
@@ -265,6 +265,8 @@
 		return 0;
 
 	req_attrs = ops->oo_id_attrs;
+	if (req_attrs == ~0)
+		req_attrs = a->ce_mask & b->ce_mask;
 
 	/* Both objects must provide all required attributes to uniquely
 	 * identify an object */
@@ -318,7 +320,7 @@
 		return 0;
 	
 	return !(ops->oo_compare(obj, filter, filter->ce_mask,
-				 LOOSE_FLAG_COMPARISON));
+				 LOOSE_COMPARISON));
 }
 
 /**
@@ -376,11 +378,6 @@
 	return obj->ce_cache;
 }
 
-inline void *nl_object_priv(struct nl_object *obj)
-{
-	return obj;
-}
-
 /** @} */
 
 /** @} */
diff --git a/lib/route/.gitignore b/lib/route/.gitignore
new file mode 100644
index 0000000..debf3b7
--- /dev/null
+++ b/lib/route/.gitignore
@@ -0,0 +1,4 @@
+pktloc_grammar.h
+pktloc_grammar.c
+pktloc_syntax.h
+pktloc_syntax.c
diff --git a/lib/route/addr.c b/lib/route/addr.c
index b8ec56c..2e72f6e 100644
--- a/lib/route/addr.c
+++ b/lib/route/addr.c
@@ -6,8 +6,8 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- *                         Baruch Even <baruch@ev-en.org>,
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2006 Baruch Even <baruch@ev-en.org>,
  *                         Mediatrix Telecom, inc. <ericb@mediatrix.com>
  */
 
@@ -48,17 +48,16 @@
  * // cannot be set for IPv6 addresses.
  * rtnl_addr_set_scope(addr, rtnl_str2scope("site"));
  *
- * // Broadcast and anycast address may be specified using the relevant
+ * // Broadcast address may be specified using the relevant
  * // functions, the address family will be verified if one of the other
  * // addresses has been set already. Currently only works for IPv4.
  * rtnl_addr_set_broadcast(addr, broadcast_addr);
- * rtnl_addr_set_anycast(addr, anycast_addr);
  *
  * // Build the netlink message and send it to the kernel, the operation will
  * // block until the operation has been completed. Alternatively the required
  * // netlink message can be built using rtnl_addr_build_add_request() to be
  * // sent out using nl_send_auto_complete().
- * rtnl_addr_add(handle, addr, 0);
+ * rtnl_addr_add(sk, addr, 0);
  *
  * // Free the memory
  * rtnl_addr_put(addr);
@@ -99,7 +98,7 @@
  * // block until the operation has been completed. Alternatively the required
  * // netlink message can be built using rtnl_addr_build_delete_request()
  * // to be sent out using nl_send_auto_complete().
- * rtnl_addr_delete(handle, addr, 0);
+ * rtnl_addr_delete(sk, addr, 0);
  *
  * // Free the memory
  * rtnl_addr_put(addr);
@@ -126,13 +125,20 @@
 #define ADDR_ATTR_PEER		0x0080
 #define ADDR_ATTR_LOCAL		0x0100
 #define ADDR_ATTR_BROADCAST	0x0200
-#define ADDR_ATTR_ANYCAST	0x0400
-#define ADDR_ATTR_MULTICAST	0x0800
+#define ADDR_ATTR_MULTICAST	0x0400
+#define ADDR_ATTR_ANYCAST	0x0800
 
 static struct nl_cache_ops rtnl_addr_ops;
 static struct nl_object_ops addr_obj_ops;
 /** @endcond */
 
+static void addr_constructor(struct nl_object *obj)
+{
+	struct rtnl_addr *addr = nl_object_priv(obj);
+
+	addr->a_scope = RT_SCOPE_NOWHERE;
+}
+
 static void addr_free_data(struct nl_object *obj)
 {
 	struct rtnl_addr *addr = nl_object_priv(obj);
@@ -143,8 +149,8 @@
 	nl_addr_put(addr->a_peer);
 	nl_addr_put(addr->a_local);
 	nl_addr_put(addr->a_bcast);
-	nl_addr_put(addr->a_anycast);
 	nl_addr_put(addr->a_multicast);
+	nl_addr_put(addr->a_anycast);
 }
 
 static int addr_clone(struct nl_object *_dst, struct nl_object *_src)
@@ -154,27 +160,25 @@
 
 	if (src->a_peer)
 		if (!(dst->a_peer = nl_addr_clone(src->a_peer)))
-			goto errout;
+			return -NLE_NOMEM;
 	
 	if (src->a_local)
 		if (!(dst->a_local = nl_addr_clone(src->a_local)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	if (src->a_bcast)
 		if (!(dst->a_bcast = nl_addr_clone(src->a_bcast)))
-			goto errout;
-
-	if (src->a_anycast)
-		if (!(dst->a_anycast = nl_addr_clone(src->a_anycast)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	if (src->a_multicast)
 		if (!(dst->a_multicast = nl_addr_clone(src->a_multicast)))
-			goto errout;
+			return -NLE_NOMEM;
+
+	if (src->a_anycast)
+		if (!(dst->a_anycast = nl_addr_clone(src->a_anycast)))
+			return -NLE_NOMEM;
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
 static struct nla_policy addr_policy[IFA_MAX+1] = {
@@ -189,21 +193,20 @@
 	struct rtnl_addr *addr;
 	struct ifaddrmsg *ifa;
 	struct nlattr *tb[IFA_MAX+1];
-	int err = -ENOMEM, peer_prefix = 0;
+	int err, peer_prefix = 0, family;
 
 	addr = rtnl_addr_alloc();
-	if (!addr) {
-		err = nl_errno(ENOMEM);
-		goto errout;
-	}
+	if (!addr)
+		return -NLE_NOMEM;
+
 	addr->ce_msgtype = nlh->nlmsg_type;
 
 	err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, addr_policy);
 	if (err < 0)
-		goto errout_free;
+		goto errout;
 
 	ifa = nlmsg_data(nlh);
-	addr->a_family = ifa->ifa_family;
+	addr->a_family = family = ifa->ifa_family;
 	addr->a_prefixlen = ifa->ifa_prefixlen;
 	addr->a_flags = ifa->ifa_flags;
 	addr->a_scope = ifa->ifa_scope;
@@ -229,18 +232,18 @@
 	}
 
 	if (tb[IFA_LOCAL]) {
-		addr->a_local = nla_get_addr(tb[IFA_LOCAL], addr->a_family);
+		addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family);
 		if (!addr->a_local)
-			goto errout_free;
+			goto errout_nomem;
 		addr->ce_mask |= ADDR_ATTR_LOCAL;
 	}
 
 	if (tb[IFA_ADDRESS]) {
 		struct nl_addr *a;
 
-		a = nla_get_addr(tb[IFA_ADDRESS], addr->a_family);
+		a = nl_addr_alloc_attr(tb[IFA_ADDRESS], family);
 		if (!a)
-			goto errout_free;
+			goto errout_nomem;
 
 		/* IPv6 sends the local address as IFA_ADDRESS with
 		 * no IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS
@@ -260,48 +263,48 @@
 			      addr->a_prefixlen);
 
 	if (tb[IFA_BROADCAST]) {
-		addr->a_bcast = nla_get_addr(tb[IFA_BROADCAST], addr->a_family);
+		addr->a_bcast = nl_addr_alloc_attr(tb[IFA_BROADCAST], family);
 		if (!addr->a_bcast)
-			goto errout_free;
+			goto errout_nomem;
 
 		addr->ce_mask |= ADDR_ATTR_BROADCAST;
 	}
 
-	if (tb[IFA_ANYCAST]) {
-		addr->a_anycast = nla_get_addr(tb[IFA_ANYCAST], addr->a_family);
-		if (!addr->a_anycast)
-			goto errout_free;
-
-		addr->ce_mask |= ADDR_ATTR_ANYCAST;
-	}
-
 	if (tb[IFA_MULTICAST]) {
-		addr->a_multicast = nla_get_addr(tb[IFA_MULTICAST],
-						 addr->a_family);
+		addr->a_multicast = nl_addr_alloc_attr(tb[IFA_MULTICAST],
+						       family);
 		if (!addr->a_multicast)
-			goto errout_free;
+			goto errout_nomem;
 
 		addr->ce_mask |= ADDR_ATTR_MULTICAST;
 	}
 
+	if (tb[IFA_ANYCAST]) {
+		addr->a_anycast = nl_addr_alloc_attr(tb[IFA_ANYCAST],
+						       family);
+		if (!addr->a_anycast)
+			goto errout_nomem;
+
+		addr->ce_mask |= ADDR_ATTR_ANYCAST;
+	}
+
 	err = pp->pp_cb((struct nl_object *) addr, pp);
-	if (err < 0)
-		goto errout_free;
-
-	err = P_ACCEPT;
-
-errout_free:
-	rtnl_addr_put(addr);
 errout:
+	rtnl_addr_put(addr);
+
 	return err;
+
+errout_nomem:
+	err = -NLE_NOMEM;
+	goto errout;
 }
 
-static int addr_request_update(struct nl_cache *cache, struct nl_handle *handle)
+static int addr_request_update(struct nl_cache *cache, struct nl_sock *sk)
 {
-	return nl_rtgen_request(handle, RTM_GETADDR, AF_UNSPEC, NLM_F_DUMP);
+	return nl_rtgen_request(sk, RTM_GETADDR, AF_UNSPEC, NLM_F_DUMP);
 }
 
-static int addr_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
+static void addr_dump_line(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_addr *addr = (struct rtnl_addr *) obj;
 	struct nl_cache *link_cache;
@@ -310,259 +313,166 @@
 	link_cache = nl_cache_mngt_require("route/link");
 
 	if (addr->ce_mask & ADDR_ATTR_LOCAL)
-		dp_dump(p, "%s",
+		nl_dump_line(p, "%s",
 			nl_addr2str(addr->a_local, buf, sizeof(buf)));
 	else
-		dp_dump(p, "none");
+		nl_dump_line(p, "none");
 
 	if (addr->ce_mask & ADDR_ATTR_PEER)
-		dp_dump(p, " peer %s",
+		nl_dump(p, " peer %s",
 			nl_addr2str(addr->a_peer, buf, sizeof(buf)));
 
-	dp_dump(p, " %s ", nl_af2str(addr->a_family, buf, sizeof(buf)));
+	nl_dump(p, " %s ", nl_af2str(addr->a_family, buf, sizeof(buf)));
 
 	if (link_cache)
-		dp_dump(p, "dev %s ",
+		nl_dump(p, "dev %s ",
 			rtnl_link_i2name(link_cache, addr->a_ifindex,
 					 buf, sizeof(buf)));
 	else
-		dp_dump(p, "dev %d ", addr->a_ifindex);
+		nl_dump(p, "dev %d ", addr->a_ifindex);
 
-	dp_dump(p, "scope %s",
+	nl_dump(p, "scope %s",
 		rtnl_scope2str(addr->a_scope, buf, sizeof(buf)));
 
 	rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf));
 	if (buf[0])
-		dp_dump(p, " <%s>", buf);
+		nl_dump(p, " <%s>", buf);
 
-	dp_dump(p, "\n");
-
-	return 1;
+	nl_dump(p, "\n");
 }
 
-static int addr_dump_full(struct nl_object *obj, struct nl_dump_params *p)
+static void addr_dump_details(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_addr *addr = (struct rtnl_addr *) obj;
-	int line = addr_dump_brief(obj, p);
 	char buf[128];
 
+	addr_dump_line(obj, p);
+
 	if (addr->ce_mask & (ADDR_ATTR_LABEL | ADDR_ATTR_BROADCAST |
-			    ADDR_ATTR_ANYCAST | ADDR_ATTR_MULTICAST)) {
-		dp_dump_line(p, line++, "  ");
+			     ADDR_ATTR_MULTICAST)) {
+		nl_dump_line(p, "  ");
 
 		if (addr->ce_mask & ADDR_ATTR_LABEL)
-			dp_dump(p, " label %s", addr->a_label);
+			nl_dump(p, " label %s", addr->a_label);
 
 		if (addr->ce_mask & ADDR_ATTR_BROADCAST)
-			dp_dump(p, " broadcast %s",
+			nl_dump(p, " broadcast %s",
 				nl_addr2str(addr->a_bcast, buf, sizeof(buf)));
 
-		if (addr->ce_mask & ADDR_ATTR_ANYCAST)
-			dp_dump(p, " anycast %s",
-				nl_addr2str(addr->a_anycast, buf,
-					      sizeof(buf)));
-
 		if (addr->ce_mask & ADDR_ATTR_MULTICAST)
-			dp_dump(p, " multicast %s",
+			nl_dump(p, " multicast %s",
 				nl_addr2str(addr->a_multicast, buf,
 					      sizeof(buf)));
 
-		dp_dump(p, "\n");
+		if (addr->ce_mask & ADDR_ATTR_ANYCAST)
+			nl_dump(p, " anycast %s",
+				nl_addr2str(addr->a_anycast, buf,
+					      sizeof(buf)));
+
+		nl_dump(p, "\n");
 	}
 
 	if (addr->ce_mask & ADDR_ATTR_CACHEINFO) {
 		struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo;
 
-		dp_dump_line(p, line++, "   valid-lifetime %s",
+		nl_dump_line(p, "   valid-lifetime %s",
 			     ci->aci_valid == 0xFFFFFFFFU ? "forever" :
 			     nl_msec2str(ci->aci_valid * 1000,
 					   buf, sizeof(buf)));
 
-		dp_dump(p, " preferred-lifetime %s\n",
+		nl_dump(p, " preferred-lifetime %s\n",
 			ci->aci_prefered == 0xFFFFFFFFU ? "forever" :
 			nl_msec2str(ci->aci_prefered * 1000,
 				      buf, sizeof(buf)));
 
-		dp_dump_line(p, line++, "   created boot-time+%s ",
+		nl_dump_line(p, "   created boot-time+%s ",
 			     nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10,
 					   buf, sizeof(buf)));
 		    
-		dp_dump(p, "last-updated boot-time+%s\n",
+		nl_dump(p, "last-updated boot-time+%s\n",
 			nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10,
 				      buf, sizeof(buf)));
 	}
-
-	return line;
 }
 
-static int addr_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+static void addr_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 {
-	return addr_dump_full(obj, p);
+	addr_dump_details(obj, p);
 }
 
-static int addr_dump_xml(struct nl_object *obj, struct nl_dump_params *p)
+static void addr_dump_env(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_addr *addr = (struct rtnl_addr *) obj;
 	struct nl_cache *link_cache;
 	char buf[128];
-	int line = 0;
 
-	dp_dump_line(p, line++, "<address>\n");
-	dp_dump_line(p, line++, "  <family>%s</family>\n",
+	nl_dump_line(p, "ADDR_FAMILY=%s\n",
 		     nl_af2str(addr->a_family, buf, sizeof(buf)));
 
 	if (addr->ce_mask & ADDR_ATTR_LOCAL)
-		dp_dump_line(p, line++, "  <local>%s</local>\n",
+		nl_dump_line(p, "ADDR_LOCAL=%s\n",
 			     nl_addr2str(addr->a_local, buf, sizeof(buf)));
 
 	if (addr->ce_mask & ADDR_ATTR_PEER)
-		dp_dump_line(p, line++, "  <peer>%s</peer>\n",
+		nl_dump_line(p, "ADDR_PEER=%s\n",
 			     nl_addr2str(addr->a_peer, buf, sizeof(buf)));
 
 	if (addr->ce_mask & ADDR_ATTR_BROADCAST)
-		dp_dump_line(p, line++, "  <broadcast>%s</broadcast>\n",
+		nl_dump_line(p, "ADDR_BROADCAST=%s\n",
 			     nl_addr2str(addr->a_bcast, buf, sizeof(buf)));
 
 	if (addr->ce_mask & ADDR_ATTR_ANYCAST)
-		dp_dump_line(p, line++, "  <anycast>%s</anycast>\n",
+		nl_dump_line(p, "ADDR_ANYCAST=%s\n",
 			     nl_addr2str(addr->a_anycast, buf, sizeof(buf)));
 
 	if (addr->ce_mask & ADDR_ATTR_MULTICAST)
-		dp_dump_line(p, line++, "  <multicast>%s</multicast>\n",
+		nl_dump_line(p, "ADDR_MULTICAST=%s\n",
 			     nl_addr2str(addr->a_multicast, buf,
 					   sizeof(buf)));
 
 	if (addr->ce_mask & ADDR_ATTR_PREFIXLEN)
-		dp_dump_line(p, line++, "  <prefixlen>%u</prefixlen>\n",
+		nl_dump_line(p, "ADDR_PREFIXLEN=%u\n",
 			     addr->a_prefixlen);
 	link_cache = nl_cache_mngt_require("route/link");
 
+	nl_dump_line(p, "ADDR_IFINDEX=%u\n", addr->a_ifindex);
 	if (link_cache)
-		dp_dump_line(p, line++, "  <device>%s</device>\n",
+		nl_dump_line(p, "ADDR_IFNAME=%s\n",
 			     rtnl_link_i2name(link_cache, addr->a_ifindex,
-			     		      buf, sizeof(buf)));
-	else
-		dp_dump_line(p, line++, "  <device>%u</device>\n",
-			     addr->a_ifindex);
+					      buf, sizeof(buf)));
 
 	if (addr->ce_mask & ADDR_ATTR_SCOPE)
-		dp_dump_line(p, line++, "  <scope>%s</scope>\n",
+		nl_dump_line(p, "ADDR_SCOPE=%s\n",
 			     rtnl_scope2str(addr->a_scope, buf, sizeof(buf)));
 
 	if (addr->ce_mask & ADDR_ATTR_LABEL)
-		dp_dump_line(p, line++, "  <label>%s</label>\n", addr->a_label);
+		nl_dump_line(p, "ADDR_LABEL=%s\n", addr->a_label);
 
 	rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf));
 	if (buf[0])
-		dp_dump_line(p, line++, "  <flags>%s</flags>\n", buf);
+		nl_dump_line(p, "ADDR_FLAGS=%s\n", buf);
 
 	if (addr->ce_mask & ADDR_ATTR_CACHEINFO) {
 		struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo;
 
-		dp_dump_line(p, line++, "  <cacheinfo>\n");
-
-		dp_dump_line(p, line++, "    <valid>%s</valid>\n",
+		nl_dump_line(p, "ADDR_CACHEINFO_VALID=%s\n",
 			     ci->aci_valid == 0xFFFFFFFFU ? "forever" :
 			     nl_msec2str(ci->aci_valid * 1000,
 					   buf, sizeof(buf)));
 
-		dp_dump_line(p, line++, "    <prefered>%s</prefered>\n",
+		nl_dump_line(p, "ADDR_CACHEINFO_PREFERED=%s\n",
 			     ci->aci_prefered == 0xFFFFFFFFU ? "forever" :
 			     nl_msec2str(ci->aci_prefered * 1000,
 					 buf, sizeof(buf)));
 
-		dp_dump_line(p, line++, "    <created>%s</created>\n",
+		nl_dump_line(p, "ADDR_CACHEINFO_CREATED=%s\n",
 			     nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10,
 					 buf, sizeof(buf)));
 
-		dp_dump_line(p, line++, "    <last-update>%s</last-update>\n",
-			     nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10,
-					 buf, sizeof(buf)));
-
-		dp_dump_line(p, line++, "  </cacheinfo>\n");
-	}
-
-	dp_dump_line(p, line++, "</address>\n");
-
-	return line;
-}
-
-static int addr_dump_env(struct nl_object *obj, struct nl_dump_params *p)
-{
-	struct rtnl_addr *addr = (struct rtnl_addr *) obj;
-	struct nl_cache *link_cache;
-	char buf[128];
-	int line = 0;
-
-	dp_dump_line(p, line++, "ADDR_FAMILY=%s\n",
-		     nl_af2str(addr->a_family, buf, sizeof(buf)));
-
-	if (addr->ce_mask & ADDR_ATTR_LOCAL)
-		dp_dump_line(p, line++, "ADDR_LOCAL=%s\n",
-			     nl_addr2str(addr->a_local, buf, sizeof(buf)));
-
-	if (addr->ce_mask & ADDR_ATTR_PEER)
-		dp_dump_line(p, line++, "ADDR_PEER=%s\n",
-			     nl_addr2str(addr->a_peer, buf, sizeof(buf)));
-
-	if (addr->ce_mask & ADDR_ATTR_BROADCAST)
-		dp_dump_line(p, line++, "ADDR_BROADCAST=%s\n",
-			     nl_addr2str(addr->a_bcast, buf, sizeof(buf)));
-
-	if (addr->ce_mask & ADDR_ATTR_ANYCAST)
-		dp_dump_line(p, line++, "ADDR_ANYCAST=%s\n",
-			     nl_addr2str(addr->a_anycast, buf, sizeof(buf)));
-
-	if (addr->ce_mask & ADDR_ATTR_MULTICAST)
-		dp_dump_line(p, line++, "ADDR_MULTICAST=%s\n",
-			     nl_addr2str(addr->a_multicast, buf,
-					   sizeof(buf)));
-
-	if (addr->ce_mask & ADDR_ATTR_PREFIXLEN)
-		dp_dump_line(p, line++, "ADDR_PREFIXLEN=%u\n",
-			     addr->a_prefixlen);
-	link_cache = nl_cache_mngt_require("route/link");
-
-	dp_dump_line(p, line++, "ADDR_IFINDEX=%u\n", addr->a_ifindex);
-	if (link_cache)
-		dp_dump_line(p, line++, "ADDR_IFNAME=%s\n",
-			     rtnl_link_i2name(link_cache, addr->a_ifindex,
-			     		      buf, sizeof(buf)));
-
-	if (addr->ce_mask & ADDR_ATTR_SCOPE)
-		dp_dump_line(p, line++, "ADDR_SCOPE=%s\n",
-			     rtnl_scope2str(addr->a_scope, buf, sizeof(buf)));
-
-	if (addr->ce_mask & ADDR_ATTR_LABEL)
-		dp_dump_line(p, line++, "ADDR_LABEL=%s\n", addr->a_label);
-
-	rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf));
-	if (buf[0])
-		dp_dump_line(p, line++, "ADDR_FLAGS=%s\n", buf);
-
-	if (addr->ce_mask & ADDR_ATTR_CACHEINFO) {
-		struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo;
-
-		dp_dump_line(p, line++, "ADDR_CACHEINFO_VALID=%s\n",
-			     ci->aci_valid == 0xFFFFFFFFU ? "forever" :
-			     nl_msec2str(ci->aci_valid * 1000,
-					   buf, sizeof(buf)));
-
-		dp_dump_line(p, line++, "ADDR_CACHEINFO_PREFERED=%s\n",
-			     ci->aci_prefered == 0xFFFFFFFFU ? "forever" :
-			     nl_msec2str(ci->aci_prefered * 1000,
-					 buf, sizeof(buf)));
-
-		dp_dump_line(p, line++, "ADDR_CACHEINFO_CREATED=%s\n",
-			     nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10,
-					 buf, sizeof(buf)));
-
-		dp_dump_line(p, line++, "ADDR_CACHEINFO_LASTUPDATE=%s\n",
+		nl_dump_line(p, "ADDR_CACHEINFO_LASTUPDATE=%s\n",
 			     nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10,
 					 buf, sizeof(buf)));
 	}
-
-	return line;
 }
 
 static int addr_compare(struct nl_object *_a, struct nl_object *_b,
@@ -580,12 +490,12 @@
 	diff |= ADDR_DIFF(LABEL,	strcmp(a->a_label, b->a_label));
 	diff |= ADDR_DIFF(PEER,		nl_addr_cmp(a->a_peer, b->a_peer));
 	diff |= ADDR_DIFF(LOCAL,	nl_addr_cmp(a->a_local, b->a_local));
-	diff |= ADDR_DIFF(ANYCAST,	nl_addr_cmp(a->a_anycast,b->a_anycast));
 	diff |= ADDR_DIFF(MULTICAST,	nl_addr_cmp(a->a_multicast,
 						    b->a_multicast));
 	diff |= ADDR_DIFF(BROADCAST,	nl_addr_cmp(a->a_bcast, b->a_bcast));
+	diff |= ADDR_DIFF(ANYCAST,	nl_addr_cmp(a->a_anycast, b->a_anycast));
 
-	if (flags & LOOSE_FLAG_COMPARISON)
+	if (flags & LOOSE_COMPARISON)
 		diff |= ADDR_DIFF(FLAGS,
 				  (a->a_flags ^ b->a_flags) & b->a_flag_mask);
 	else
@@ -607,7 +517,6 @@
 	__ADD(ADDR_ATTR_PEER, peer)
 	__ADD(ADDR_ATTR_LOCAL, local)
 	__ADD(ADDR_ATTR_BROADCAST, broadcast)
-	__ADD(ADDR_ATTR_ANYCAST, anycast)
 	__ADD(ADDR_ATTR_MULTICAST, multicast)
 };
 
@@ -639,25 +548,15 @@
  * @{
  */
 
-struct nl_cache *rtnl_addr_alloc_cache(struct nl_handle *handle)
+int rtnl_addr_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
 {
-	struct nl_cache *cache;
-	
-	cache = nl_cache_alloc(&rtnl_addr_ops);
-	if (!cache)
-		return NULL;
-
-	if (handle && nl_cache_refill(handle, cache) < 0) {
-		nl_cache_free(cache);
-		return NULL;
-	}
-
-	return cache;
+	return nl_cache_alloc_and_fill(&rtnl_addr_ops, sk, result);
 }
 
 /** @} */
 
-static struct nl_msg *build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags)
+static int build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags,
+			  struct nl_msg **result)
 {
 	struct nl_msg *msg;
 	struct ifaddrmsg am = {
@@ -680,7 +579,7 @@
 
 	msg = nlmsg_alloc_simple(cmd, flags);
 	if (!msg)
-		goto nla_put_failure;
+		return -NLE_NOMEM;
 
 	if (nlmsg_append(msg, &am, sizeof(am), NLMSG_ALIGNTO) < 0)
 		goto nla_put_failure;
@@ -690,7 +589,7 @@
 
 	if (tmpl->ce_mask & ADDR_ATTR_PEER)
 		NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_peer);
-	else
+	else if (tmpl->ce_mask & ADDR_ATTR_LOCAL)
 		NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_local);
 
 	if (tmpl->ce_mask & ADDR_ATTR_LABEL)
@@ -699,14 +598,22 @@
 	if (tmpl->ce_mask & ADDR_ATTR_BROADCAST)
 		NLA_PUT_ADDR(msg, IFA_BROADCAST, tmpl->a_bcast);
 
-	if (tmpl->ce_mask & ADDR_ATTR_ANYCAST)
-		NLA_PUT_ADDR(msg, IFA_ANYCAST, tmpl->a_anycast);
+	if (tmpl->ce_mask & ADDR_ATTR_CACHEINFO) {
+		struct ifa_cacheinfo ca = {
+			.ifa_valid = tmpl->a_cacheinfo.aci_valid,
+			.ifa_prefered = tmpl->a_cacheinfo.aci_prefered,
+		};
 
-	return msg;
+		NLA_PUT(msg, IFA_CACHEINFO, sizeof(ca), &ca);
+	}
+
+
+	*result = msg;
+	return 0;
 
 nla_put_failure:
 	nlmsg_free(msg);
-	return NULL;
+	return -NLE_MSGSIZE;
 }
 
 /**
@@ -718,6 +625,7 @@
  * Build netlink request message to request addition of new address
  * @arg addr		Address object representing the new address.
  * @arg flags		Additional netlink message flags.
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting the addition of a new
  * address. The netlink message header isn't fully equipped with
@@ -732,25 +640,24 @@
  * which case a host scope is used if not specified otherwise.
  *
  * @note Free the memory after usage using nlmsg_free().
- * @return Newly allocated netlink message or NULL if an error occured.
+ *
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg *rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags)
+int rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags,
+				struct nl_msg **result)
 {
 	int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY |
 		       ADDR_ATTR_PREFIXLEN | ADDR_ATTR_LOCAL;
 
-	if ((addr->ce_mask & required) != required) {
-		nl_error(EINVAL, "Missing mandatory attributes, required are: "
-				 "ifindex, family, prefixlen, local address.");
-		return NULL;
-	}
+	if ((addr->ce_mask & required) != required)
+		return -NLE_MISSING_ATTR;
 	
-	return build_addr_msg(addr, RTM_NEWADDR, NLM_F_CREATE | flags);
+	return build_addr_msg(addr, RTM_NEWADDR, NLM_F_CREATE | flags, result);
 }
 
 /**
  * Request addition of new address
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg addr		Address object representing the new address.
  * @arg flags		Additional netlink message flags.
  *
@@ -762,21 +669,20 @@
  *
  * @return 0 on sucess or a negative error if an error occured.
  */
-int rtnl_addr_add(struct nl_handle *handle, struct rtnl_addr *addr, int flags)
+int rtnl_addr_add(struct nl_sock *sk, struct rtnl_addr *addr, int flags)
 {
 	struct nl_msg *msg;
 	int err;
 
-	msg = rtnl_addr_build_add_request(addr, flags);
-	if (!msg)
-		return nl_get_errno();
+	if ((err = rtnl_addr_build_add_request(addr, flags, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
 	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -790,6 +696,7 @@
  * Build a netlink request message to request deletion of an address
  * @arg addr		Address object to be deleteted.
  * @arg flags		Additional netlink message flags.
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting a deletion of an address.
  * The netlink message header isn't fully equipped with all relevant
@@ -806,24 +713,23 @@
  *   - peer address (rtnl_addr_set_peer(), IPv4 only)
  *
  * @note Free the memory after usage using nlmsg_free().
- * @return Newly allocated netlink message or NULL if an error occured.
+ *
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg *rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags)
+int rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags,
+				   struct nl_msg **result)
 {
 	int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY;
 
-	if ((addr->ce_mask & required) != required) {
-		nl_error(EINVAL, "Missing mandatory attributes, required are: "
-				 "ifindex, family");
-		return NULL;
-	}
-	
-	return build_addr_msg(addr, RTM_DELADDR, flags);
+	if ((addr->ce_mask & required) != required)
+		return -NLE_MISSING_ATTR;
+
+	return build_addr_msg(addr, RTM_DELADDR, flags, result);
 }
 
 /**
  * Request deletion of an address
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg addr		Address object to be deleted.
  * @arg flags		Additional netlink message flags.
  *
@@ -835,22 +741,20 @@
  *
  * @return 0 on sucess or a negative error if an error occured.
  */
-int rtnl_addr_delete(struct nl_handle *handle, struct rtnl_addr *addr,
-		     int flags)
+int rtnl_addr_delete(struct nl_sock *sk, struct rtnl_addr *addr, int flags)
 {
 	struct nl_msg *msg;
 	int err;
 
-	msg = rtnl_addr_build_delete_request(addr, flags);
-	if (!msg)
-		return nl_get_errno();
+	if ((err = rtnl_addr_build_delete_request(addr, flags, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
 	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -860,10 +764,15 @@
  * @{
  */
 
-void rtnl_addr_set_label(struct rtnl_addr *addr, const char *label)
+int rtnl_addr_set_label(struct rtnl_addr *addr, const char *label)
 {
-	strncpy(addr->a_label, label, sizeof(addr->a_label) - 1);
+	if (strlen(label) > sizeof(addr->a_label) - 1)
+		return -NLE_RANGE;
+
+	strcpy(addr->a_label, label);
 	addr->ce_mask |= ADDR_ATTR_LABEL;
+
+	return 0;
 }
 
 char *rtnl_addr_get_label(struct rtnl_addr *addr)
@@ -882,10 +791,7 @@
 
 int rtnl_addr_get_ifindex(struct rtnl_addr *addr)
 {
-	if (addr->ce_mask & ADDR_ATTR_IFINDEX)
-		return addr->a_ifindex;
-	else
-		return RTNL_LINK_NOT_FOUND;
+	return addr->a_ifindex;
 }
 
 void rtnl_addr_set_family(struct rtnl_addr *addr, int family)
@@ -896,10 +802,7 @@
 
 int rtnl_addr_get_family(struct rtnl_addr *addr)
 {
-	if (addr->ce_mask & ADDR_ATTR_FAMILY)
-		return addr->a_family;
-	else
-		return AF_UNSPEC;
+	return addr->a_family;
 }
 
 void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefix)
@@ -910,10 +813,7 @@
 
 int rtnl_addr_get_prefixlen(struct rtnl_addr *addr)
 {
-	if (addr->ce_mask & ADDR_ATTR_PREFIXLEN)
-		return addr->a_prefixlen;
-	else
-		return -1;
+	return addr->a_prefixlen;
 }
 
 void rtnl_addr_set_scope(struct rtnl_addr *addr, int scope)
@@ -924,10 +824,7 @@
 
 int rtnl_addr_get_scope(struct rtnl_addr *addr)
 {
-	if (addr->ce_mask & ADDR_ATTR_SCOPE)
-		return addr->a_scope;
-	else
-		return -1;
+	return addr->a_scope;
 }
 
 void rtnl_addr_set_flags(struct rtnl_addr *addr, unsigned int flags)
@@ -954,7 +851,7 @@
 {
 	if (addr->ce_mask & ADDR_ATTR_FAMILY) {
 		if (new->a_family != addr->a_family)
-			return nl_error(EINVAL, "Address family mismatch");
+			return -NLE_AF_MISMATCH;
 	} else
 		addr->a_family = new->a_family;
 
@@ -985,10 +882,7 @@
 
 struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *addr)
 {
-	if (addr->ce_mask & ADDR_ATTR_LOCAL)
-		return addr->a_local;
-	else
-		return NULL;
+	return addr->a_local;
 }
 
 int rtnl_addr_set_peer(struct rtnl_addr *addr, struct nl_addr *peer)
@@ -1003,10 +897,7 @@
 
 struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *addr)
 {
-	if (addr->ce_mask & ADDR_ATTR_PEER)
-		return addr->a_peer;
-	else
-		return NULL;
+	return addr->a_peer;
 }
 
 int rtnl_addr_set_broadcast(struct rtnl_addr *addr, struct nl_addr *bcast)
@@ -1016,24 +907,7 @@
 
 struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *addr)
 {
-	if (addr->ce_mask & ADDR_ATTR_BROADCAST)
-		return addr->a_bcast;
-	else
-		return NULL;
-}
-
-int rtnl_addr_set_anycast(struct rtnl_addr *addr, struct nl_addr *anycast)
-{
-	return __assign_addr(addr, &addr->a_anycast, anycast,
-			     ADDR_ATTR_ANYCAST);
-}
-
-struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *addr)
-{
-	if (addr->ce_mask & ADDR_ATTR_ANYCAST)
-		return addr->a_anycast;
-	else
-		return NULL;
+	return addr->a_bcast;
 }
 
 int rtnl_addr_set_multicast(struct rtnl_addr *addr, struct nl_addr *multicast)
@@ -1044,10 +918,56 @@
 
 struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *addr)
 {
-	if (addr->ce_mask & ADDR_ATTR_MULTICAST)
-		return addr->a_multicast;
+	return addr->a_multicast;
+}
+
+int rtnl_addr_set_anycast(struct rtnl_addr *addr, struct nl_addr *anycast)
+{
+	return __assign_addr(addr, &addr->a_anycast, anycast,
+			     ADDR_ATTR_ANYCAST);
+}
+
+struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *addr)
+{
+	return addr->a_anycast;
+}
+
+uint32_t rtnl_addr_get_valid_lifetime(struct rtnl_addr *addr)
+{
+	if (addr->ce_mask & ADDR_ATTR_CACHEINFO)
+		return addr->a_cacheinfo.aci_valid;
 	else
-		return NULL;
+		return 0xFFFFFFFFU;
+}
+
+void rtnl_addr_set_valid_lifetime(struct rtnl_addr *addr, uint32_t lifetime)
+{
+	addr->a_cacheinfo.aci_valid = lifetime;
+	addr->ce_mask |= ADDR_ATTR_CACHEINFO;
+}
+
+uint32_t rtnl_addr_get_preferred_lifetime(struct rtnl_addr *addr)
+{
+	if (addr->ce_mask & ADDR_ATTR_CACHEINFO)
+		return addr->a_cacheinfo.aci_prefered;
+	else
+		return 0xFFFFFFFFU;
+}
+
+void rtnl_addr_set_preferred_lifetime(struct rtnl_addr *addr, uint32_t lifetime)
+{
+	addr->a_cacheinfo.aci_prefered = lifetime;
+	addr->ce_mask |= ADDR_ATTR_CACHEINFO;
+}
+
+uint32_t rtnl_addr_get_create_time(struct rtnl_addr *addr)
+{
+	return addr->a_cacheinfo.aci_cstamp;
+}
+
+uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *addr)
+{
+	return addr->a_cacheinfo.aci_tstamp;
 }
 
 /** @} */
@@ -1059,6 +979,9 @@
 
 static struct trans_tbl addr_flags[] = {
 	__ADD(IFA_F_SECONDARY, secondary)
+	__ADD(IFA_F_NODAD, nodad)
+	__ADD(IFA_F_OPTIMISTIC, optimistic)
+	__ADD(IFA_F_HOMEADDRESS, homeaddress)
 	__ADD(IFA_F_DEPRECATED, deprecated)
 	__ADD(IFA_F_TENTATIVE, tentative)
 	__ADD(IFA_F_PERMANENT, permanent)
@@ -1080,18 +1003,19 @@
 static struct nl_object_ops addr_obj_ops = {
 	.oo_name		= "route/addr",
 	.oo_size		= sizeof(struct rtnl_addr),
+	.oo_constructor		= addr_constructor,
 	.oo_free_data		= addr_free_data,
 	.oo_clone		= addr_clone,
-	.oo_dump[NL_DUMP_BRIEF] = addr_dump_brief,
-	.oo_dump[NL_DUMP_FULL]  = addr_dump_full,
-	.oo_dump[NL_DUMP_STATS] = addr_dump_stats,
-	.oo_dump[NL_DUMP_XML]	= addr_dump_xml,
-	.oo_dump[NL_DUMP_ENV]	= addr_dump_env,
+	.oo_dump = {
+	    [NL_DUMP_LINE] 	= addr_dump_line,
+	    [NL_DUMP_DETAILS]	= addr_dump_details,
+	    [NL_DUMP_STATS]	= addr_dump_stats,
+	    [NL_DUMP_ENV]	= addr_dump_env,
+	},
 	.oo_compare		= addr_compare,
 	.oo_attrs2str		= addr_attrs2str,
 	.oo_id_attrs		= (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX |
-				   ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN |
-				   ADDR_ATTR_PEER),
+				   ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN),
 };
 
 static struct nl_af_group addr_groups[] = {
diff --git a/lib/route/class.c b/lib/route/class.c
index 7966b09..ddf2d2e 100644
--- a/lib/route/class.c
+++ b/lib/route/class.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -36,7 +36,7 @@
 
 	class = rtnl_class_alloc();
 	if (!class) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 	class->ce_msgtype = n->nlmsg_type;
@@ -53,26 +53,20 @@
 	}
 
 	err = pp->pp_cb((struct nl_object *) class, pp);
-	if (err < 0)
-		goto errout_free;
-
-	err = P_ACCEPT;
-
 errout_free:
 	rtnl_class_put(class);
 errout:
 	return err;
 }
 
-static int class_request_update(struct nl_cache *cache,
-				struct nl_handle *handle)
+static int class_request_update(struct nl_cache *cache, struct nl_sock *sk)
 {
 	struct tcmsg tchdr = {
 		.tcm_family = AF_UNSPEC,
 		.tcm_ifindex = cache->c_iarg1,
 	};
 
-	return nl_send_simple(handle, RTM_GETTCLASS, NLM_F_DUMP, &tchdr,
+	return nl_send_simple(sk, RTM_GETTCLASS, NLM_F_DUMP, &tchdr,
 			      sizeof(tchdr));
 }
 
@@ -81,15 +75,15 @@
  * @{
  */
 
-static struct nl_msg *class_build(struct rtnl_class *class, int type, int flags)
+static int class_build(struct rtnl_class *class, int type, int flags,
+		       struct nl_msg **result)
 {
 	struct rtnl_class_ops *cops;
-	struct nl_msg *msg;
 	int err;
 
-	msg = tca_build_msg((struct rtnl_tca *) class, type, flags);
-	if (!msg)
-		goto errout;
+	err = tca_build_msg((struct rtnl_tca *) class, type, flags, result);
+	if (err < 0)
+		return err;
 
 	cops = rtnl_class_lookup_ops(class);
 	if (cops && cops->co_get_opts) {
@@ -97,23 +91,24 @@
 		
 		opts = cops->co_get_opts(class);
 		if (opts) {
-			err = nla_put_nested(msg, TCA_OPTIONS, opts);
+			err = nla_put_nested(*result, TCA_OPTIONS, opts);
 			nlmsg_free(opts);
 			if (err < 0)
 				goto errout;
 		}
 	}
 
-	return msg;
+	return 0;
 errout:
-	nlmsg_free(msg);
-	return NULL;
+	nlmsg_free(*result);
+	return err;
 }
 
 /**
  * Build a netlink message to add a new class
  * @arg class		class to add 
  * @arg flags		additional netlink message flags
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting an addition of a class.
  * The netlink message header isn't fully equipped with all relevant
@@ -123,16 +118,17 @@
  * Common message flags
  *   - NLM_F_REPLACE - replace possibly existing classes
  *
- * @return New netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg *rtnl_class_build_add_request(struct rtnl_class *class, int flags)
+int rtnl_class_build_add_request(struct rtnl_class *class, int flags,
+				 struct nl_msg **result)
 {
-	return class_build(class, RTM_NEWTCLASS, NLM_F_CREATE | flags);
+	return class_build(class, RTM_NEWTCLASS, NLM_F_CREATE | flags, result);
 }
 
 /**
  * Add a new class
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg class		class to delete
  * @arg flags		additional netlink message flags
  *
@@ -145,22 +141,74 @@
  *
  * @return 0 on success or a negative error code
  */
-int rtnl_class_add(struct nl_handle *handle, struct rtnl_class *class,
-		   int flags)
+int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags)
 {
 	struct nl_msg *msg;
 	int err;
 
-	msg = rtnl_class_build_add_request(class, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_class_build_add_request(class, flags, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
+	return wait_for_ack(sk);
+}
+
+int rtnl_class_build_delete_request(struct rtnl_class *class,
+									struct nl_msg **result)
+{
+	struct nl_msg *msg;
+	struct tcmsg tchdr;
+	int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
+
+	if ((class->ce_mask & required) != required)
+		BUG();
+
+	msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0);
+	if (!msg)
+		return -NLE_NOMEM;
+
+	tchdr.tcm_family = AF_UNSPEC;
+	tchdr.tcm_handle = class->c_handle;
+	tchdr.tcm_parent = class->c_parent;
+	tchdr.tcm_ifindex = class->c_ifindex;
+	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
+		nlmsg_free(msg);
+		return -NLE_MSGSIZE;
+	}
+
+	*result = msg;
+	return 0;
+}
+
+/**
+ * Delete a class
+ * @arg sk		Netlink socket.
+ * @arg class		class to delete
+ *
+ * Builds a netlink message by calling rtnl_class_build_delete_request(),
+ * sends the request to the kernel and waits for the ACK to be
+ * received and thus blocks until the request has been processed.
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = rtnl_class_build_delete_request(class, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
 	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -172,7 +220,7 @@
 
 /**
  * Build a class cache including all classes attached to the specified interface
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg ifindex		interface index of the link the classes are
  *                      attached to.
  *
@@ -181,22 +229,49 @@
  *
  * @return The cache or NULL if an error has occured.
  */
-struct nl_cache * rtnl_class_alloc_cache(struct nl_handle *handle, int ifindex)
+int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex,
+			   struct nl_cache **result)
 {
 	struct nl_cache * cache;
+	int err;
 	
 	cache = nl_cache_alloc(&rtnl_class_ops);
 	if (!cache)
-		return NULL;
+		return -NLE_NOMEM;
 
 	cache->c_iarg1 = ifindex;
 	
-	if (handle && nl_cache_refill(handle, cache) < 0) {
+	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
 		nl_cache_free(cache);
-		return NULL;
+		return err;
 	}
 
-	return cache;
+	*result = cache;
+	return 0;
+}
+
+/**
+ * Look up class by its handle in the provided cache
+ * @arg cache		class cache
+ * @arg ifindex		interface the class is attached to
+ * @arg handle		class handle
+ * @return pointer to class inside the cache or NULL if no match was found.
+ */
+struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex,
+								  uint32_t handle)
+{
+	struct rtnl_class *class;
+	
+	if (cache->c_ops != &rtnl_class_ops)
+		return NULL;
+
+	nl_list_for_each_entry(class, &cache->c_items, ce_list) {
+		if (class->c_handle == handle && class->c_ifindex == ifindex) {
+			nl_object_get((struct nl_object *) class);
+			return class;
+		}
+	}
+	return NULL;
 }
 
 /** @} */
diff --git a/lib/route/class_api.c b/lib/route/class_api.c
index c814486..374cf0f 100644
--- a/lib/route/class_api.c
+++ b/lib/route/class_api.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -43,7 +43,7 @@
 
 	for (op = &class_ops_list; (o = *op) != NULL; op = &o->co_next)
 		if (!strcasecmp(cops->co_kind, o->co_kind))
-			return nl_errno(EEXIST);
+			return -NLE_EXIST;
 
 	cops->co_next = NULL;
 	*op = cops;
@@ -64,7 +64,7 @@
 			break;
 
 	if (!o)
-		return nl_errno(ENOENT);
+		return -NLE_OBJ_NOTFOUND;
 
 	*op = cops->co_next;
 
diff --git a/lib/route/class_obj.c b/lib/route/class_obj.c
index 0601bdf..5c2e5be 100644
--- a/lib/route/class_obj.c
+++ b/lib/route/class_obj.c
@@ -55,62 +55,54 @@
 	return err;
 }
 
-static int class_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
+static void class_dump_line(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_class *class = (struct rtnl_class *) obj;
 	struct rtnl_class_ops *cops;
 
-	int line = tca_dump_brief((struct rtnl_tca *) class, "class", p, 0);
+	tca_dump_line((struct rtnl_tca *) class, "class", p);
 
 	cops = rtnl_class_lookup_ops(class);
-	if (cops && cops->co_dump[NL_DUMP_BRIEF])
-		line = cops->co_dump[NL_DUMP_BRIEF](class, p, line);
-	dp_dump(p, "\n");
-
-	return line;
+	if (cops && cops->co_dump[NL_DUMP_LINE])
+		cops->co_dump[NL_DUMP_LINE](class, p);
+	nl_dump(p, "\n");
 }
 
-static int class_dump_full(struct nl_object *obj, struct nl_dump_params *p)
+static void class_dump_details(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_class *class = (struct rtnl_class *) obj;
 	struct rtnl_class_ops *cops;
-	int line;
 
-	line = class_dump_brief(obj, p);
-	line = tca_dump_full((struct rtnl_tca *) class, p, line);
+	class_dump_line(obj, p);
+	tca_dump_details((struct rtnl_tca *) class, p);
 	
 	if (class->c_info) {
 		char buf[32];
-		dp_dump(p, "child-qdisc %s ",
+		nl_dump(p, "child-qdisc %s ",
 			rtnl_tc_handle2str(class->c_info, buf, sizeof(buf)));
 	}
 
 	cops = rtnl_class_lookup_ops(class);
-	if (cops && cops->co_dump[NL_DUMP_FULL])
-		line = cops->co_dump[NL_DUMP_FULL](class, p, line);
+	if (cops && cops->co_dump[NL_DUMP_DETAILS])
+		cops->co_dump[NL_DUMP_DETAILS](class, p);
 	else if (!class->c_info)
-		dp_dump(p, "noop (no leaf qdisc)");
+		nl_dump(p, "noop (no leaf qdisc)");
 
-	dp_dump(p, "\n");
-
-	return line;
+	nl_dump(p, "\n");
 }
 
-static int class_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+static void class_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_class *class = (struct rtnl_class *) obj;
 	struct rtnl_class_ops *cops;
-	int line;
 
-	line = class_dump_full(obj, p);
-	line = tca_dump_stats((struct rtnl_tca *) class, p, line);
-	dp_dump(p, "\n");
+	class_dump_details(obj, p);
+	tca_dump_stats((struct rtnl_tca *) class, p);
+	nl_dump(p, "\n");
 
 	cops = rtnl_class_lookup_ops(class);
 	if (cops && cops->co_dump[NL_DUMP_STATS])
-		line = cops->co_dump[NL_DUMP_STATS](class, p, line);
-
-	return line;
+		cops->co_dump[NL_DUMP_STATS](class, p);
 }
 
 /**
@@ -277,9 +269,11 @@
 	.oo_size		= sizeof(struct rtnl_class),
 	.oo_free_data         	= class_free_data,
 	.oo_clone		= class_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= class_dump_brief,
-	.oo_dump[NL_DUMP_FULL]	= class_dump_full,
-	.oo_dump[NL_DUMP_STATS]	= class_dump_stats,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= class_dump_line,
+	    [NL_DUMP_DETAILS]	= class_dump_details,
+	    [NL_DUMP_STATS]	= class_dump_stats,
+	},
 	.oo_compare		= tca_compare,
 	.oo_id_attrs		= (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
 };
diff --git a/lib/route/classifier.c b/lib/route/cls.c
similarity index 68%
rename from lib/route/classifier.c
rename to lib/route/cls.c
index df6d3ae..cbf0345 100644
--- a/lib/route/classifier.c
+++ b/lib/route/cls.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -38,13 +38,13 @@
 static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
 			  struct nlmsghdr *nlh, struct nl_parser_param *pp)
 {
-	int err;
-	struct rtnl_cls *cls;
 	struct rtnl_cls_ops *cops;
+	struct rtnl_cls *cls;
+	int err;
 
 	cls = rtnl_cls_alloc();
 	if (!cls) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 	cls->ce_msgtype = nlh->nlmsg_type;
@@ -57,25 +57,17 @@
 	cls->c_protocol = ntohs(TC_H_MIN(cls->c_info));
 
 	cops = rtnl_cls_lookup_ops(cls);
-	if (cops && cops->co_msg_parser) {
-		err = cops->co_msg_parser(cls);
-		if (err < 0)
-			goto errout_free;
-	}
-
-	err = pp->pp_cb((struct nl_object *) cls, pp);
-	if (err < 0)
+	if (cops && cops->co_msg_parser && (err = cops->co_msg_parser(cls)) < 0)
 		goto errout_free;
 
-	err = P_ACCEPT;
-
+	err = pp->pp_cb((struct nl_object *) cls, pp);
 errout_free:
 	rtnl_cls_put(cls);
 errout:
 	return err;
 }
 
-static int cls_request_update(struct nl_cache *cache, struct nl_handle *handle)
+static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk)
 {
 	struct tcmsg tchdr = {
 		.tcm_family = AF_UNSPEC,
@@ -83,44 +75,48 @@
 		.tcm_parent = cache->c_iarg2,
 	};
 
-	return nl_send_simple(handle, RTM_GETTFILTER, NLM_F_DUMP, &tchdr,
+	return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr,
 			      sizeof(tchdr));
 }
 
 
-static struct nl_msg *cls_build(struct rtnl_cls *cls, int type, int flags)
+static int cls_build(struct rtnl_cls *cls, int type, int flags,
+		     struct nl_msg **result)
 {
-	struct nl_msg *msg;
 	struct rtnl_cls_ops *cops;
 	int err, prio, proto;
 	struct tcmsg *tchdr;
 
-	msg = tca_build_msg((struct rtnl_tca *) cls, type, flags);
-	if (!msg)
-		goto errout;
+	err = tca_build_msg((struct rtnl_tca *) cls, type, flags, result);
+	if (err < 0)
+		return err;
 
-	tchdr = nlmsg_data(nlmsg_hdr(msg));
+	tchdr = nlmsg_data(nlmsg_hdr(*result));
 	prio = rtnl_cls_get_prio(cls);
 	proto = rtnl_cls_get_protocol(cls);
-	tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto)),
+	tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto));
 
 	cops = rtnl_cls_lookup_ops(cls);
 	if (cops && cops->co_get_opts) {
 		struct nl_msg *opts;
-		
-		opts = cops->co_get_opts(cls);
-		if (opts) {
-			err = nla_put_nested(msg, TCA_OPTIONS, opts);
-			nlmsg_free(opts);
-			if (err < 0)
-				goto errout;
+
+		if (!(opts = nlmsg_alloc())) {
+			err = -NLE_NOMEM;
+			goto errout;
 		}
+
+		if (!(err = cops->co_get_opts(cls, opts)))
+			err = nla_put_nested(*result, TCA_OPTIONS, opts);
+
+		nlmsg_free(opts);
+		if (err < 0)
+			goto errout;
 	}
 
-	return msg;
+	return 0;
 errout:
-	nlmsg_free(msg);
-	return NULL;
+	nlmsg_free(*result);
+	return err;
 }
 
 /**
@@ -132,6 +128,7 @@
  * Build a netlink message to add a new classifier
  * @arg cls		classifier to add
  * @arg flags		additional netlink message flags
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting an addition of a classifier
  * The netlink message header isn't fully equipped with all relevant
@@ -140,16 +137,17 @@
  * the new classifier set via \c rtnl_cls_set_* functions. \a opts
  * may point to the clsasifier specific options.
  *
- * @return New netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg * rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags)
+int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags,
+			       struct nl_msg **result)
 {
-	return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags);
+	return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags, result);
 }
 
 /**
  * Add a new classifier
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg cls 		classifier to add
  * @arg flags		additional netlink message flags
  *
@@ -159,43 +157,44 @@
  *
  * @return 0 on sucess or a negative error if an error occured.
  */
-int rtnl_cls_add(struct nl_handle *handle, struct rtnl_cls *cls, int flags)
+int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
 {
-	int err;
 	struct nl_msg *msg;
+	int err;
 	
-	msg = rtnl_cls_build_add_request(cls, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0)
+		return err;
 	
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return nl_wait_for_ack(sk);
 }
 
 /**
  * Build a netlink message to change classifier attributes
  * @arg cls		classifier to change
  * @arg flags		additional netlink message flags
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting a change of a neigh
  * attributes. The netlink message header isn't fully equipped with
  * all relevant fields and must thus be sent out via nl_send_auto_complete()
  * or supplemented as needed.
  *
- * @return The netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg *rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags)
+int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags,
+				  struct nl_msg **result)
 {
-	return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags);
+	return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result);
 }
 
 /**
  * Change a classifier
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg cls		classifier to change
  * @arg flags		additional netlink message flags
  *
@@ -205,45 +204,45 @@
  *
  * @return 0 on sucess or a negative error if an error occured.
  */
-int rtnl_cls_change(struct nl_handle *handle, struct rtnl_cls *cls,
-		    int flags)
+int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
 {
-	int err;
 	struct nl_msg *msg;
+	int err;
 	
-	msg = rtnl_cls_build_change_request(cls, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0)
+		return err;
 	
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return nl_wait_for_ack(sk);
 }
 
 /**
  * Build a netlink request message to delete a classifier
  * @arg cls		classifier to delete
  * @arg flags		additional netlink message flags
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting a deletion of a classifier.
  * The netlink message header isn't fully equipped with all relevant
  * fields and must thus be sent out via nl_send_auto_complete()
  * or supplemented as needed.
  *
- * @return New netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg *rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags)
+int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags,
+				  struct nl_msg **result)
 {
-	return cls_build(cls, RTM_DELTFILTER, flags);
+	return cls_build(cls, RTM_DELTFILTER, flags, result);
 }
 
 
 /**
  * Delete a classifier
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg cls		classifier to delete
  * @arg flags		additional netlink message flags
  *
@@ -253,21 +252,20 @@
  *
  * @return 0 on sucess or a negative error if an error occured.
  */
-int rtnl_cls_delete(struct nl_handle *handle, struct rtnl_cls *cls, int flags)
+int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
 {
-	int err;
 	struct nl_msg *msg;
+	int err;
 	
-	msg = rtnl_cls_build_delete_request(cls, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0)
+		return err;
 	
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return nl_wait_for_ack(sk);
 }
 
 /** @} */
@@ -280,36 +278,37 @@
 /**
  * Build a classifier cache including all classifiers attached to the
  * specified class/qdisc on eht specified interface.
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg ifindex		interface index of the link the classes are
  *                      attached to.
  * @arg parent          parent qdisc/class
+ * @arg result		Pointer to store resulting cache.
  *
  * Allocates a new cache, initializes it properly and updates it to
  * include all classes attached to the specified interface.
  *
  * @note The caller is responsible for destroying and freeing the
  *       cache after using it.
- * @return The cache or NULL if an error has occured.
+ * @return 0 on success or a negative error code.
  */
-struct nl_cache *rtnl_cls_alloc_cache(struct nl_handle *handle,
-				      int ifindex, uint32_t parent)
+int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent,			 struct nl_cache **result)
 {
 	struct nl_cache * cache;
+	int err;
 	
-	cache = nl_cache_alloc(&rtnl_cls_ops);
-	if (cache == NULL)
-		return NULL;
+	if (!(cache = nl_cache_alloc(&rtnl_cls_ops)))
+		return -NLE_NOMEM;
 
 	cache->c_iarg1 = ifindex;
 	cache->c_iarg2 = parent;
 	
-	if (handle && nl_cache_refill(handle, cache) < 0) {
+	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
 		nl_cache_free(cache);
-		return NULL;
+		return err;
 	}
 
-	return cache;
+	*result = cache;
+	return 0;
 }
 
 /** @} */
diff --git a/lib/route/cls/basic.c b/lib/route/cls/basic.c
new file mode 100644
index 0000000..1460b72
--- /dev/null
+++ b/lib/route/cls/basic.c
@@ -0,0 +1,211 @@
+/*
+ * lib/route/cls/basic.c	Basic Classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cls
+ * @defgroup basic Basic Classifier
+ *
+ * @par Introduction
+ * The basic classifier is the simplest form of a classifier. It does
+ * not have any special classification capabilities, instead it can be
+ * used to classify exclusively based on extended matches or to
+ * create a "catch-all" filter.
+ *
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/classifier-modules.h>
+#include <netlink/route/cls/basic.h>
+#include <netlink/route/cls/ematch.h>
+
+struct rtnl_basic
+{
+	uint32_t			b_classid;
+	struct rtnl_ematch_tree *	b_ematch;
+	int				b_mask;
+};
+
+/** @cond SKIP */
+#define BASIC_ATTR_CLASSID	0x001
+#define BASIC_ATTR_EMATCH	0x002
+/** @endcond */
+
+static struct nla_policy basic_policy[TCA_FW_MAX+1] = {
+	[TCA_BASIC_CLASSID]	= { .type = NLA_U32 },
+	[TCA_BASIC_EMATCHES]	= { .type = NLA_NESTED },
+	[TCA_BASIC_ACT]		= { .type = NLA_NESTED },
+	[TCA_BASIC_POLICE]	= { .type = NLA_NESTED },
+};
+
+static int basic_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
+{
+	return -NLE_OPNOTSUPP;
+}
+
+static void basic_free_data(struct rtnl_cls *cls)
+{
+	struct rtnl_basic *basic = rtnl_cls_data(cls);
+
+	rtnl_ematch_tree_free(basic->b_ematch);
+}
+
+static int basic_msg_parser(struct rtnl_cls *cls)
+{
+	struct nlattr *tb[TCA_BASIC_MAX + 1];
+	struct rtnl_basic *basic = rtnl_cls_data(cls);
+	int err;
+
+	err = tca_parse(tb, TCA_BASIC_MAX, (struct rtnl_tca *) cls, basic_policy);
+	if (err < 0)
+		return err;
+
+	if (tb[TCA_BASIC_CLASSID]) {
+		basic->b_classid = nla_get_u32(tb[TCA_BASIC_CLASSID]);
+		basic->b_mask |= BASIC_ATTR_CLASSID;
+	}
+
+	if (tb[TCA_BASIC_EMATCHES]) {
+		if ((err = rtnl_ematch_parse(tb[TCA_BASIC_EMATCHES],
+					     &basic->b_ematch)) < 0)
+			return err;
+
+		if (basic->b_ematch)
+			basic->b_mask |= BASIC_ATTR_EMATCH;
+	}
+
+	if (tb[TCA_BASIC_ACT]) {
+		/* XXX */
+	}
+
+	if (tb[TCA_BASIC_POLICE]) {
+		/* XXX */
+	}
+
+	return 0;
+}
+
+static void basic_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
+{
+	struct rtnl_basic *b = rtnl_cls_data(cls);
+	char buf[32];
+
+	if (b->b_mask & BASIC_ATTR_EMATCH)
+		nl_dump(p, " ematch");
+	else
+		nl_dump(p, " match-all");
+
+	if (b->b_mask & BASIC_ATTR_CLASSID)
+		nl_dump(p, " classify-to %s",
+			rtnl_tc_handle2str(b->b_classid, buf, sizeof(buf)));
+}
+
+static void basic_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
+{
+	struct rtnl_basic *b = rtnl_cls_data(cls);
+
+	if (b->b_mask & BASIC_ATTR_EMATCH) {
+		nl_dump(p, "\n");
+		nl_dump_line(p, "    ematch ");
+		rtnl_ematch_tree_dump(b->b_ematch, p);
+	} else
+		nl_dump(p, "no options.\n");
+}
+
+static int basic_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
+{
+	struct rtnl_basic *b = rtnl_cls_data(cls);
+
+	if (!(b->b_mask & BASIC_ATTR_CLASSID))
+		return -NLE_MISSING_ATTR;
+
+	NLA_PUT_U32(msg, TCA_BASIC_CLASSID, b->b_classid);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_NOMEM;
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+int rtnl_basic_set_classid(struct rtnl_cls *cls, uint32_t classid)
+{
+	struct rtnl_basic *b = rtnl_cls_data(cls);
+
+	b->b_classid = classid;
+	b->b_mask |= BASIC_ATTR_CLASSID;
+
+	return 0;
+}
+
+uint32_t rtnl_basic_get_classid(struct rtnl_cls *cls)
+{
+	struct rtnl_basic *b = rtnl_cls_data(cls);
+
+	return b->b_classid;
+}
+
+int rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree)
+{
+	struct rtnl_basic *b = rtnl_cls_data(cls);
+
+	if (b->b_ematch) {
+		rtnl_ematch_tree_free(b->b_ematch);
+		b->b_mask &= ~BASIC_ATTR_EMATCH;
+	}
+
+	b->b_ematch = tree;
+
+	if (tree)
+		b->b_mask |= BASIC_ATTR_EMATCH;
+
+	return 0;
+}
+
+struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *cls)
+{
+	struct rtnl_basic *b = rtnl_cls_data(cls);
+	return b->b_ematch;
+}
+
+/** @} */
+
+static struct rtnl_cls_ops basic_ops = {
+	.co_kind		= "basic",
+	.co_size		= sizeof(struct rtnl_basic),
+	.co_msg_parser		= basic_msg_parser,
+	.co_clone		= basic_clone,
+	.co_free_data		= basic_free_data,
+	.co_get_opts		= basic_get_opts,
+	.co_dump = {
+	    [NL_DUMP_LINE]	= basic_dump_line,
+	    [NL_DUMP_DETAILS]	= basic_dump_details,
+	},
+};
+
+static void __init basic_init(void)
+{
+	rtnl_cls_register(&basic_ops);
+}
+
+static void __exit basic_exit(void)
+{
+	rtnl_cls_unregister(&basic_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/cgroup.c b/lib/route/cls/cgroup.c
new file mode 100644
index 0000000..e5f38b8
--- /dev/null
+++ b/lib/route/cls/cgroup.c
@@ -0,0 +1,141 @@
+/*
+ * lib/route/cls/cgroup.c	Control Groups Classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cls_api
+ * @defgroup cgroup Control Groups Classifier
+ *
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/classifier-modules.h>
+#include <netlink/route/cls/cgroup.h>
+#include <netlink/route/cls/ematch.h>
+
+/** @cond SKIP */
+#define CGROUP_ATTR_EMATCH      0x001
+/** @endcond */
+
+static struct nla_policy cgroup_policy[TCA_CGROUP_MAX+1] = {
+	[TCA_CGROUP_EMATCHES]	= { .type = NLA_NESTED },
+};
+
+static void cgroup_free_data(struct rtnl_cls *cls)
+{
+	struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+
+	rtnl_ematch_tree_free(cg->cg_ematch);
+}
+
+static int cgroup_msg_parser(struct rtnl_cls *cls)
+{
+	struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+	struct nlattr *tb[TCA_CGROUP_MAX + 1];
+	int err;
+
+	err = tca_parse(tb, TCA_CGROUP_MAX, (struct rtnl_tca *) cls,
+			cgroup_policy);
+	if (err < 0)
+		return err;
+
+	if (tb[TCA_CGROUP_EMATCHES]) {
+		if ((err = rtnl_ematch_parse(tb[TCA_CGROUP_EMATCHES],
+					     &cg->cg_ematch)) < 0)
+			return err;
+		cg->cg_mask |= CGROUP_ATTR_EMATCH;
+	}
+
+#if 0
+	TODO:
+	TCA_CGROUP_ACT,
+	TCA_CGROUP_POLICE,
+#endif
+
+	return 0;
+}
+
+static void cgroup_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
+{
+	struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+
+	if (cg->cg_mask & CGROUP_ATTR_EMATCH)
+		nl_dump(p, " ematch");
+	else
+		nl_dump(p, " match-all");
+}
+
+static void cgroup_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
+{
+	struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+
+	if (cg->cg_mask & CGROUP_ATTR_EMATCH) {
+		nl_dump(p, "\n");
+		nl_dump_line(p, "    ematch ");
+		rtnl_ematch_tree_dump(cg->cg_ematch, p);
+	}
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+int rtnl_cgroup_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree)
+{
+	struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+
+	if (cg->cg_ematch) {
+		rtnl_ematch_tree_free(cg->cg_ematch);
+		cg->cg_mask &= ~CGROUP_ATTR_EMATCH;
+	}
+
+	cg->cg_ematch = tree;
+
+	if (tree)
+		cg->cg_mask |= CGROUP_ATTR_EMATCH;
+
+	return 0;
+}
+
+struct rtnl_ematch_tree *rtnl_cgroup_get_ematch(struct rtnl_cls *cls)
+{
+	struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+	return cg->cg_ematch;
+}
+
+static struct rtnl_cls_ops cgroup_ops = {
+	.co_kind		= "cgroup",
+	.co_size		= sizeof(struct rtnl_cgroup),
+	.co_msg_parser		= cgroup_msg_parser,
+	.co_free_data		= cgroup_free_data,
+	.co_dump = {
+	    [NL_DUMP_LINE]	= cgroup_dump_line,
+	    [NL_DUMP_DETAILS]	= cgroup_dump_details,
+	},
+};
+
+static void __init cgroup_init(void)
+{
+	rtnl_cls_register(&cgroup_ops);
+}
+
+static void __exit cgroup_exit(void)
+{
+	rtnl_cls_unregister(&cgroup_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/ematch.c b/lib/route/cls/ematch.c
new file mode 100644
index 0000000..cb77b16
--- /dev/null
+++ b/lib/route/cls/ematch.c
@@ -0,0 +1,410 @@
+/*
+ * lib/route/cls/ematch.c	Extended Matches
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cls
+ * @defgroup ematch Extended Match
+ *
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/classifier-modules.h>
+#include <netlink/route/cls/ematch.h>
+
+/**
+ * @name Module Registration
+ * @{
+ */
+
+static NL_LIST_HEAD(ematch_ops_list);
+
+/**
+ * Register ematch module
+ * @arg ops		Module operations.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
+{
+	if (rtnl_ematch_lookup_ops(ops->eo_kind))
+		return -NLE_EXIST;
+
+	nl_list_add_tail(&ops->eo_list, &ematch_ops_list);
+
+	return 0;
+}
+
+/**
+ * Unregister ematch module
+ * @arg ops		Module operations.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_ematch_unregister(struct rtnl_ematch_ops *ops)
+{
+	struct rtnl_ematch_ops *o;
+
+	nl_list_for_each_entry(o, &ematch_ops_list, eo_list) {
+		if (ops->eo_kind == o->eo_kind) {
+			nl_list_del(&o->eo_list);
+			return 0;
+		}
+	}
+
+	return -NLE_OBJ_NOTFOUND;
+}
+
+/**
+ * Lookup ematch module by kind
+ * @arg kind		Module kind.
+ *
+ * @return Module operations or NULL if not found.
+ */
+struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
+{
+	struct rtnl_ematch_ops *ops;
+
+	nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
+		if (ops->eo_kind == kind)
+			return ops;
+
+	return NULL;
+}
+
+/**
+ * Lookup ematch module by name
+ * @arg name		Name of ematch module.
+ *
+ * @return Module operations or NULL if not fuond.
+ */
+struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name)
+{
+	struct rtnl_ematch_ops *ops;
+
+	nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
+		if (!strcasecmp(ops->eo_name, name))
+			return ops;
+
+	return NULL;
+}
+
+/** @} */
+
+/**
+ * @name Match
+ */
+
+struct rtnl_ematch *rtnl_ematch_alloc(struct rtnl_ematch_ops *ops)
+{
+	struct rtnl_ematch *e;
+	size_t len = sizeof(*e) + (ops ? ops->eo_datalen : 0);
+
+	if (!(e = calloc(1, len)))
+		return NULL;
+
+	NL_INIT_LIST_HEAD(&e->e_list);
+	NL_INIT_LIST_HEAD(&e->e_childs);
+
+	if (ops) {
+		e->e_ops = ops;
+		e->e_kind = ops->eo_kind;
+	}
+
+	return e;
+}
+
+/**
+ * Add ematch to the end of the parent's list of children.
+ * @arg parent		Parent ematch.
+ * @arg child		Ematch to be added as new child of parent.
+ */
+void rtnl_ematch_add_child(struct rtnl_ematch *parent,
+			   struct rtnl_ematch *child)
+{
+	nl_list_add_tail(&child->e_list, &parent->e_childs);
+}
+
+/**
+ * Remove ematch from the list it is linked to.
+ * @arg ematch		Ematch to be unlinked.
+ */
+void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
+{
+	nl_list_del(&ematch->e_list);
+}
+
+void rtnl_ematch_free(struct rtnl_ematch *ematch)
+{
+	if (!ematch)
+		return;
+
+	free(ematch);
+}
+
+void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
+{
+	ematch->e_flags |= flags;
+}
+
+void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags)
+{
+	ematch->e_flags &= ~flags;
+}
+
+uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch)
+{
+	return ematch->e_flags;
+}
+
+void *rtnl_ematch_data(struct rtnl_ematch *ematch)
+{
+	return ematch->e_data;
+}
+
+/** @} */
+
+/**
+ * @name Tree
+ */
+
+struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid)
+{
+	struct rtnl_ematch_tree *tree;
+
+	if (!(tree = calloc(1, sizeof(*tree))))
+		return NULL;
+
+	NL_INIT_LIST_HEAD(&tree->et_list);
+	tree->et_progid = progid;
+
+	return tree;
+}
+
+static void free_ematch_list(struct nl_list_head *head)
+{
+	struct rtnl_ematch *pos, *next;
+
+	nl_list_for_each_entry_safe(pos, next, head, e_list) {
+		if (!nl_list_empty(&pos->e_childs))
+			free_ematch_list(&pos->e_childs);
+		rtnl_ematch_free(pos);
+	}
+}
+
+void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree)
+{
+	if (!tree)
+		return;
+
+	free_ematch_list(&tree->et_list);
+	free(tree);
+}
+
+void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *tree,
+			       struct rtnl_ematch *ematch)
+{
+	nl_list_add_tail(&ematch->e_list, &tree->et_list);
+}
+
+static inline uint32_t container_ref(struct rtnl_ematch *ematch)
+{
+	return *((uint32_t *) rtnl_ematch_data(ematch));
+}
+
+static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos,
+		     struct nl_list_head *root)
+{
+	struct rtnl_ematch *ematch;
+	int i;
+
+	for (i = pos; i < nmatches; i++) {
+		ematch = index[i];
+
+		nl_list_add_tail(&ematch->e_list, root);
+
+		if (ematch->e_kind == TCF_EM_CONTAINER)
+			link_tree(index, nmatches, container_ref(ematch),
+				  &ematch->e_childs);
+
+		if (!(ematch->e_flags & TCF_EM_REL_MASK))
+			return 0;
+	}
+
+	/* Last entry in chain can't possibly have no relation */
+	return -NLE_INVAL;
+}
+
+static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = {
+	[TCA_EMATCH_TREE_HDR]  = { .minlen=sizeof(struct tcf_ematch_tree_hdr) },
+	[TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED },
+};
+
+/**
+ * Parse ematch netlink attributes
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
+{
+	struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1];
+	struct tcf_ematch_tree_hdr *thdr;
+	struct rtnl_ematch_tree *tree;
+	struct rtnl_ematch **index;
+	int nmatches = 0, err, remaining;
+
+	err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy);
+	if (err < 0)
+		return err;
+
+	if (!tb[TCA_EMATCH_TREE_HDR])
+		return -NLE_MISSING_ATTR;
+
+	thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]);
+
+	/* Ignore empty trees */
+	if (thdr->nmatches == 0)
+		return 0;
+
+	if (!tb[TCA_EMATCH_TREE_LIST])
+		return -NLE_MISSING_ATTR;
+
+	if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) /
+			      nla_total_size(sizeof(struct tcf_ematch_hdr))))
+		return -NLE_INVAL;
+
+	if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *))))
+		return -NLE_NOMEM;
+
+	if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) {
+		err = -NLE_NOMEM;
+		goto errout;
+	}
+
+	nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) {
+		struct rtnl_ematch_ops *ops;
+		struct tcf_ematch_hdr *hdr;
+		struct rtnl_ematch *ematch;
+		void *data;
+		size_t len;
+
+		if (nla_len(a) < sizeof(*hdr)) {
+			err = -NLE_INVAL;
+			goto errout;
+		}
+
+		if (nmatches >= thdr->nmatches) {
+			err = -NLE_RANGE;
+			goto errout;
+		}
+
+		hdr = nla_data(a);
+		data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
+		len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));
+
+		ops = rtnl_ematch_lookup_ops(hdr->kind);
+		if (ops && ops->eo_datalen && len < ops->eo_datalen) {
+			err = -NLE_INVAL;
+			goto errout;
+		}
+
+		if (!(ematch = rtnl_ematch_alloc(ops))) {
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+
+		ematch->e_id = hdr->matchid;
+		ematch->e_kind = hdr->kind;
+		ematch->e_flags = hdr->flags;
+
+		if (ops && (err = ops->eo_parse(ematch, data, len)) < 0)
+			goto errout;
+
+		if (hdr->kind == TCF_EM_CONTAINER &&
+		    container_ref(ematch) >= thdr->nmatches) {
+			err = -NLE_INVAL;
+			goto errout;
+		}
+
+		index[nmatches++] = ematch;
+	}
+
+	if (nmatches != thdr->nmatches) {
+		err = -NLE_INVAL;
+		goto errout;
+	}
+
+	err = link_tree(index, nmatches, 0, &tree->et_list);
+	if (err < 0)
+		goto errout;
+
+	free(index);
+	*result = tree;
+
+	return 0;
+
+errout:
+	rtnl_ematch_tree_free(tree);
+	free(index);
+	return err;
+}
+
+static void dump_ematch_sequence(struct nl_list_head *head,
+				 struct nl_dump_params *p)
+{
+	struct rtnl_ematch *match;
+
+	nl_list_for_each_entry(match, head, e_list) {
+		if (match->e_flags & TCF_EM_INVERT)
+			nl_dump(p, "NOT ");
+
+		if (match->e_kind == TCF_EM_CONTAINER) {
+			nl_dump(p, "(");
+			dump_ematch_sequence(&match->e_childs, p);
+			nl_dump(p, ")");
+		} else if (!match->e_ops) {
+			nl_dump(p, "[unknown ematch %d]", match->e_kind);
+		} else {
+			nl_dump(p, "%s(", match->e_ops->eo_name);
+
+			if (match->e_ops->eo_dump)
+				match->e_ops->eo_dump(match, p);
+
+			nl_dump(p, ")");
+		}
+
+		switch (match->e_flags & TCF_EM_REL_MASK) {
+		case TCF_EM_REL_AND:
+			nl_dump(p, " AND ");
+			break;
+		case TCF_EM_REL_OR:
+			nl_dump(p, " OR ");
+			break;
+		default:
+			/* end of first level ematch sequence */
+			return;
+		}
+	}
+}
+
+void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree,
+			   struct nl_dump_params *p)
+{
+	dump_ematch_sequence(&tree->et_list, p);
+	nl_dump(p, "\n");
+}
+
+/** @} */
+
+/** @} */
diff --git a/lib/route/cls/ematch/cmp.c b/lib/route/cls/ematch/cmp.c
new file mode 100644
index 0000000..ec25320
--- /dev/null
+++ b/lib/route/cls/ematch/cmp.c
@@ -0,0 +1,116 @@
+/*
+ * lib/route/cls/ematch/cmp.c	Simple packet data comparison ematch
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup ematch
+ * @defgroup em_cmp Simple packet data comparison
+ *
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+#include <linux/tc_ematch/tc_em_cmp.h>
+
+void rtnl_ematch_cmp_set(struct rtnl_ematch *ematch,
+			 struct tcf_em_cmp *cfg)
+{
+	memcpy(rtnl_ematch_data(ematch), cfg, sizeof(*cfg));
+}
+
+struct tcf_em_cmp *rtnl_ematch_cmp_get(struct rtnl_ematch *ematch)
+{
+	return rtnl_ematch_data(ematch);
+}
+
+static const char *align_txt(struct tcf_em_cmp *cmp)
+{
+	switch (cmp->align) {
+	case TCF_EM_ALIGN_U8:
+		return "u8";
+	case TCF_EM_ALIGN_U16:
+		return (cmp->flags & TCF_EM_CMP_TRANS) ? "h16" : "u16";
+	case TCF_EM_ALIGN_U32:
+		return (cmp->flags & TCF_EM_CMP_TRANS) ? "h32" : "u32";
+	default:
+		return (cmp->flags & TCF_EM_CMP_TRANS) ? "h?" : "u?";
+	}
+}
+
+static const char *layer_txt(struct tcf_em_cmp *cmp)
+{
+	switch (cmp->layer) {
+	case TCF_LAYER_LINK:
+		return "link";
+	case TCF_LAYER_NETWORK:
+		return "network";
+	case TCF_LAYER_TRANSPORT:
+		return "transport";
+	default:
+		return "?";
+	}
+}
+
+static const char *relation_txt(struct tcf_em_cmp *cmp)
+{
+	switch (cmp->opnd) {
+	case TCF_EM_OPND_EQ:
+		return "eq";
+	case TCF_EM_OPND_LT:
+		return "lt";
+	case TCF_EM_OPND_GT:
+		return "gt";
+	default:
+		return "?";
+	}
+}
+
+static int cmp_parse(struct rtnl_ematch *m, void *data, size_t len)
+{
+	memcpy(rtnl_ematch_data(m), data, len);
+
+	return 0;
+}
+
+static void cmp_dump(struct rtnl_ematch *m, struct nl_dump_params *p)
+{
+	struct tcf_em_cmp *cmp = rtnl_ematch_data(m);
+
+	nl_dump(p, "%s at %s+%u ",
+		align_txt(cmp), layer_txt(cmp), cmp->off);
+
+	if (cmp->mask)
+		nl_dump(p, "& 0x%x ", cmp->mask);
+
+	nl_dump(p, "%s %u", relation_txt(cmp), cmp->val);
+}
+
+static struct rtnl_ematch_ops cmp_ops = {
+	.eo_kind	= TCF_EM_CMP,
+	.eo_name	= "cmp",
+	.eo_datalen	= sizeof(struct tcf_em_cmp),
+	.eo_parse	= cmp_parse,
+	.eo_dump	= cmp_dump,
+};
+
+static void __init cmp_init(void)
+{
+	rtnl_ematch_register(&cmp_ops);
+}
+
+static void __exit cmp_exit(void)
+{
+	rtnl_ematch_unregister(&cmp_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/ematch/container.c b/lib/route/cls/ematch/container.c
new file mode 100644
index 0000000..54d836f
--- /dev/null
+++ b/lib/route/cls/ematch/container.c
@@ -0,0 +1,39 @@
+/*
+ * lib/route/cls/ematch/container.c	Container Ematch
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+
+static int container_parse(struct rtnl_ematch *m, void *data, size_t len)
+{
+	memcpy(m->e_data, data, sizeof(uint32_t));
+
+	return 0;
+}
+
+static struct rtnl_ematch_ops container_ops = {
+	.eo_kind	= TCF_EM_CONTAINER,
+	.eo_name	= "container",
+	.eo_datalen	= sizeof(uint32_t),
+	.eo_parse	= container_parse,
+};
+
+static void __init container_init(void)
+{
+	rtnl_ematch_register(&container_ops);
+}
+
+static void __exit container_exit(void)
+{
+	rtnl_ematch_unregister(&container_ops);
+}
diff --git a/lib/route/cls/fw.c b/lib/route/cls/fw.c
index 7ca7619..8cf25b9 100644
--- a/lib/route/cls/fw.c
+++ b/lib/route/cls/fw.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2006 Petr Gotthard <petr.gotthard@siemens.com>
  * Copyright (c) 2006 Siemens AG Oesterreich
  */
@@ -32,19 +32,6 @@
 #define FW_ATTR_INDEV        0x008
 /** @endcond */
 
-static inline struct rtnl_fw *fw_cls(struct rtnl_cls *cls)
-{
-	return (struct rtnl_fw *) cls->c_subdata;
-}
-
-static inline struct rtnl_fw *fw_alloc(struct rtnl_cls *cls)
-{
-	if (!cls->c_subdata)
-		cls->c_subdata = calloc(1, sizeof(struct rtnl_fw));
-
-	return fw_cls(cls);
-}
-
 static struct nla_policy fw_policy[TCA_FW_MAX+1] = {
 	[TCA_FW_CLASSID]	= { .type = NLA_U32 },
 	[TCA_FW_INDEV]		= { .type = NLA_STRING,
@@ -53,34 +40,30 @@
 
 static int fw_msg_parser(struct rtnl_cls *cls)
 {
-	int err;
+	struct rtnl_fw *f = rtnl_cls_data(cls);
 	struct nlattr *tb[TCA_FW_MAX + 1];
-	struct rtnl_fw *f;
+	int err;
 
 	err = tca_parse(tb, TCA_FW_MAX, (struct rtnl_tca *) cls, fw_policy);
 	if (err < 0)
 		return err;
 
-	f = fw_alloc(cls);
-	if (!f)
-		goto errout_nomem;
-
 	if (tb[TCA_FW_CLASSID]) {
 		f->cf_classid = nla_get_u32(tb[TCA_FW_CLASSID]);
 		f->cf_mask |= FW_ATTR_CLASSID;
 	}
 
 	if (tb[TCA_FW_ACT]) {
-		f->cf_act = nla_get_data(tb[TCA_FW_ACT]);
+		f->cf_act = nl_data_alloc_attr(tb[TCA_FW_ACT]);
 		if (!f->cf_act)
-			goto errout_nomem;
+			return -NLE_NOMEM;
 		f->cf_mask |= FW_ATTR_ACTION;
 	}
 
 	if (tb[TCA_FW_POLICE]) {
-		f->cf_police = nla_get_data(tb[TCA_FW_POLICE]);
+		f->cf_police = nl_data_alloc_attr(tb[TCA_FW_POLICE]);
 		if (!f->cf_police)
-			goto errout_nomem;
+			return -NLE_NOMEM;
 		f->cf_mask |= FW_ATTR_POLICE;
 	}
 
@@ -90,120 +73,68 @@
 	}
 
 	return 0;
-
-errout_nomem:
-	err = nl_errno(ENOMEM);
-
-	return err;
 }
 
 static void fw_free_data(struct rtnl_cls *cls)
 {
-	struct rtnl_fw *f = fw_cls(cls);
-
-	if (!f)
-		return;
+	struct rtnl_fw *f = rtnl_cls_data(cls);
 
 	nl_data_free(f->cf_act);
 	nl_data_free(f->cf_police);
-
-	free(cls->c_subdata);
 }
 
 static int fw_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
 {
-	struct rtnl_fw *dst, *src = fw_cls(_src);
+	struct rtnl_fw *dst = rtnl_cls_data(_dst);
+	struct rtnl_fw *src = rtnl_cls_data(_src);
 
-	if (!src)
-		return 0;
-
-	dst = fw_alloc(_dst);
-	if (!dst)
-		return nl_errno(ENOMEM);
-
-	if (src->cf_act)
-		if (!(dst->cf_act = nl_data_clone(src->cf_act)))
-			goto errout;
+	if (src->cf_act && !(dst->cf_act = nl_data_clone(src->cf_act)))
+		return -NLE_NOMEM;
 	
-	if (src->cf_police)
-		if (!(dst->cf_police = nl_data_clone(src->cf_police)))
-			goto errout;
+	if (src->cf_police && !(dst->cf_police = nl_data_clone(src->cf_police)))
+		return -NLE_NOMEM;
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
-static int fw_dump_brief(struct rtnl_cls *cls, struct nl_dump_params *p,
-			  int line)
+static void fw_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
 {
-	struct rtnl_fw *f = fw_cls(cls);
+	struct rtnl_fw *f = rtnl_cls_data(cls);
 	char buf[32];
 
-	if (!f)
-		goto ignore;
-
 	if (f->cf_mask & FW_ATTR_CLASSID)
-		dp_dump(p, " target %s",
+		nl_dump(p, " target %s",
 			rtnl_tc_handle2str(f->cf_classid, buf, sizeof(buf)));
-
-ignore:
-	return line;
 }
 
-static int fw_dump_full(struct rtnl_cls *cls, struct nl_dump_params *p,
-			 int line)
+static void fw_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
 {
-	struct rtnl_fw *f = fw_cls(cls);
-
-	if (!f)
-		goto ignore;
+	struct rtnl_fw *f = rtnl_cls_data(cls);
 
 	if (f->cf_mask & FW_ATTR_INDEV)
-		dp_dump(p, "indev %s ", f->cf_indev);
-
-ignore:
-	return line;
+		nl_dump(p, "indev %s ", f->cf_indev);
 }
 
-static int fw_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p,
-			  int line)
+static int fw_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
 {
-	struct rtnl_fw *f = fw_cls(cls);
-
-	if (!f)
-		goto ignore;
-
-ignore:
-	return line;
-}
-
-static struct nl_msg *fw_get_opts(struct rtnl_cls *cls)
-{
-	struct rtnl_fw *f;
-	struct nl_msg *msg;
+	struct rtnl_fw *f = rtnl_cls_data(cls);
 	
-	f = fw_cls(cls);
-	if (!f)
-		return NULL;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return NULL;
-
 	if (f->cf_mask & FW_ATTR_CLASSID)
-		nla_put_u32(msg, TCA_FW_CLASSID, f->cf_classid);
+		NLA_PUT_U32(msg, TCA_FW_CLASSID, f->cf_classid);
 
 	if (f->cf_mask & FW_ATTR_ACTION)
-		nla_put_data(msg, TCA_FW_ACT, f->cf_act);
+		NLA_PUT_DATA(msg, TCA_FW_ACT, f->cf_act);
 
 	if (f->cf_mask & FW_ATTR_POLICE)
-		nla_put_data(msg, TCA_FW_POLICE, f->cf_police);
+		NLA_PUT_DATA(msg, TCA_FW_POLICE, f->cf_police);
 
 	if (f->cf_mask & FW_ATTR_INDEV)
-		nla_put_string(msg, TCA_FW_INDEV, f->cf_indev);
+		NLA_PUT_STRING(msg, TCA_FW_INDEV, f->cf_indev);
 
-	return msg;
+	return 0;
+
+nla_put_failure:
+	return -NLE_NOMEM;
 }
 
 /**
@@ -213,12 +144,8 @@
 
 int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid)
 {
-	struct rtnl_fw *f;
+	struct rtnl_fw *f = rtnl_cls_data(cls);
 	
-	f = fw_alloc(cls);
-	if (!f)
-		return nl_errno(ENOMEM);
-
 	f->cf_classid = classid;
 	f->cf_mask |= FW_ATTR_CLASSID;
 
@@ -229,13 +156,15 @@
 
 static struct rtnl_cls_ops fw_ops = {
 	.co_kind		= "fw",
+	.co_size		= sizeof(struct rtnl_fw),
 	.co_msg_parser		= fw_msg_parser,
 	.co_free_data		= fw_free_data,
 	.co_clone		= fw_clone,
 	.co_get_opts		= fw_get_opts,
-	.co_dump[NL_DUMP_BRIEF]	= fw_dump_brief,
-	.co_dump[NL_DUMP_FULL]	= fw_dump_full,
-	.co_dump[NL_DUMP_STATS]	= fw_dump_stats,
+	.co_dump = {
+	    [NL_DUMP_LINE]	= fw_dump_line,
+	    [NL_DUMP_DETAILS]	= fw_dump_details,
+	},
 };
 
 static void __init fw_init(void)
diff --git a/lib/route/cls/u32.c b/lib/route/cls/u32.c
index 596e63f..80b8851 100644
--- a/lib/route/cls/u32.c
+++ b/lib/route/cls/u32.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
  * Copyright (c) 2005-2006 Siemens AG Oesterreich
  */
@@ -40,19 +40,6 @@
 #define U32_ATTR_INDEV        0x100
 /** @endcond */
 
-static inline struct rtnl_u32 *u32_cls(struct rtnl_cls *cls)
-{
-	return (struct rtnl_u32 *) cls->c_subdata;
-}
-
-static inline struct rtnl_u32 *u32_alloc(struct rtnl_cls *cls)
-{
-	if (!cls->c_subdata)
-		cls->c_subdata = calloc(1, sizeof(struct rtnl_u32));
-
-	return u32_cls(cls);
-}
-
 static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u)
 {
 	return (struct tc_u32_sel *) u->cu_selector->d_data;
@@ -79,25 +66,21 @@
 
 static int u32_msg_parser(struct rtnl_cls *cls)
 {
-	int err;
+	struct rtnl_u32 *u = rtnl_cls_data(cls);
 	struct nlattr *tb[TCA_U32_MAX + 1];
-	struct rtnl_u32 *u;
+	int err;
 
 	err = tca_parse(tb, TCA_U32_MAX, (struct rtnl_tca *) cls, u32_policy);
 	if (err < 0)
 		return err;
 
-	u = u32_alloc(cls);
-	if (!u)
-		goto errout_nomem;
-
 	if (tb[TCA_U32_DIVISOR]) {
 		u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
 		u->cu_mask |= U32_ATTR_DIVISOR;
 	}
 
 	if (tb[TCA_U32_SEL]) {
-		u->cu_selector = nla_get_data(tb[TCA_U32_SEL]);
+		u->cu_selector = nl_data_alloc_attr(tb[TCA_U32_SEL]);
 		if (!u->cu_selector)
 			goto errout_nomem;
 		u->cu_mask |= U32_ATTR_SELECTOR;
@@ -119,14 +102,14 @@
 	}
 
 	if (tb[TCA_U32_ACT]) {
-		u->cu_act = nla_get_data(tb[TCA_U32_ACT]);
+		u->cu_act = nl_data_alloc_attr(tb[TCA_U32_ACT]);
 		if (!u->cu_act)
 			goto errout_nomem;
 		u->cu_mask |= U32_ATTR_ACTION;
 	}
 
 	if (tb[TCA_U32_POLICE]) {
-		u->cu_police = nla_get_data(tb[TCA_U32_POLICE]);
+		u->cu_police = nl_data_alloc_attr(tb[TCA_U32_POLICE]);
 		if (!u->cu_police)
 			goto errout_nomem;
 		u->cu_mask |= U32_ATTR_POLICE;
@@ -137,8 +120,7 @@
 		int pcnt_size;
 
 		if (!tb[TCA_U32_SEL]) {
-			err = nl_error(EINVAL, "Missing TCA_U32_SEL required "
-					       "for TCA_U32_PCNT");
+			err = -NLE_MISSING_ATTR;
 			goto errout;
 		}
 		
@@ -146,11 +128,11 @@
 		pcnt_size = sizeof(struct tc_u32_pcnt) +
 				(sel->nkeys * sizeof(uint64_t));
 		if (nla_len(tb[TCA_U32_PCNT]) < pcnt_size) {
-			err = nl_error(EINVAL, "Invalid size for TCA_U32_PCNT");
+			err = -NLE_INVAL;
 			goto errout;
 		}
 
-		u->cu_pcnt = nla_get_data(tb[TCA_U32_PCNT]);
+		u->cu_pcnt = nl_data_alloc_attr(tb[TCA_U32_PCNT]);
 		if (!u->cu_pcnt)
 			goto errout_nomem;
 		u->cu_mask |= U32_ATTR_PCNT;
@@ -164,79 +146,56 @@
 	return 0;
 
 errout_nomem:
-	err = nl_errno(ENOMEM);
+	err = -NLE_NOMEM;
 errout:
 	return err;
 }
 
 static void u32_free_data(struct rtnl_cls *cls)
 {
-	struct rtnl_u32 *u = u32_cls(cls);
-
-	if (!u)
-		return;
+	struct rtnl_u32 *u = rtnl_cls_data(cls);
 
 	nl_data_free(u->cu_selector);
 	nl_data_free(u->cu_act);
 	nl_data_free(u->cu_police);
 	nl_data_free(u->cu_pcnt);
-
-	free(cls->c_subdata);
 }
 
 static int u32_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
 {
-	struct rtnl_u32 *dst, *src = u32_cls(_src);
+	struct rtnl_u32 *dst = rtnl_cls_data(_dst);
+	struct rtnl_u32 *src = rtnl_cls_data(_src);
 
-	if (!src)
-		return 0;
+	if (src->cu_selector &&
+	    !(dst->cu_selector = nl_data_clone(src->cu_selector)))
+		return -NLE_NOMEM;
 
-	dst = u32_alloc(_dst);
-	if (!dst)
-		return nl_errno(ENOMEM);
+	if (src->cu_act && !(dst->cu_act = nl_data_clone(src->cu_act)))
+		return -NLE_NOMEM;
 
-	if (src->cu_selector)
-		if (!(dst->cu_selector = nl_data_clone(src->cu_selector)))
-			goto errout;
+	if (src->cu_police && !(dst->cu_police = nl_data_clone(src->cu_police)))
+		return -NLE_NOMEM;
 
-	if (src->cu_act)
-		if (!(dst->cu_act = nl_data_clone(src->cu_act)))
-			goto errout;
-
-	if (src->cu_police)
-		if (!(dst->cu_police = nl_data_clone(src->cu_police)))
-			goto errout;
-
-	if (src->cu_pcnt)
-		if (!(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
-			goto errout;
+	if (src->cu_pcnt && !(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
+		return -NLE_NOMEM;
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
-static int u32_dump_brief(struct rtnl_cls *cls, struct nl_dump_params *p,
-			  int line)
+static void u32_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
 {
-	struct rtnl_u32 *u = u32_cls(cls);
+	struct rtnl_u32 *u = rtnl_cls_data(cls);
 	char buf[32];
 
-	if (!u)
-		goto ignore;
-
 	if (u->cu_mask & U32_ATTR_DIVISOR)
-		dp_dump(p, " divisor %u", u->cu_divisor);
+		nl_dump(p, " divisor %u", u->cu_divisor);
 	else if (u->cu_mask & U32_ATTR_CLASSID)
-		dp_dump(p, " target %s",
+		nl_dump(p, " target %s",
 			rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf)));
-
-ignore:
-	return line;
 }
 
-static int print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
-			  struct rtnl_cls *cls, struct rtnl_u32 *u, int line)
+static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
+			   struct rtnl_cls *cls, struct rtnl_u32 *u)
 {
 	int i;
 	struct tc_u32_key *key;
@@ -246,23 +205,23 @@
 		 * exports the selector if no divisor is set but hash offset
 		 * and hash mask make only sense in hash filters with divisor
 		 * set */
-		dp_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask);
+		nl_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask);
 	}
 
 	if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
-		dp_dump(p, " offset at %u", sel->off);
+		nl_dump(p, " offset at %u", sel->off);
 
 		if (sel->flags & TC_U32_VAROFFSET)
-			dp_dump(p, " variable (at %u & 0x%x) >> %u",
+			nl_dump(p, " variable (at %u & 0x%x) >> %u",
 				sel->offoff, ntohs(sel->offmask), sel->offshift);
 	}
 
 	if (sel->flags) {
 		int flags = sel->flags;
-		dp_dump(p, " <");
+		nl_dump(p, " <");
 
 #define PRINT_FLAG(f) if (flags & TC_U32_##f) { \
-	flags &= ~TC_U32_##f; dp_dump(p, #f "%s", flags ? "," : ""); }
+	flags &= ~TC_U32_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
 
 		PRINT_FLAG(TERMINAL);
 		PRINT_FLAG(OFFSET);
@@ -270,66 +229,56 @@
 		PRINT_FLAG(EAT);
 #undef PRINT_FLAG
 
-		dp_dump(p, ">");
+		nl_dump(p, ">");
 	}
 		
 	
 	for (i = 0; i < sel->nkeys; i++) {
 		key = (struct tc_u32_key *) ((char *) sel + sizeof(*sel)) + i;
 
-		dp_dump(p, "\n");
-		dp_dump_line(p, line++, "      match key at %s%u ",
-		key->offmask ? "nexthdr+" : "", key->off);
+		nl_dump(p, "\n");
+		nl_dump_line(p, "      match key at %s%u ",
+			key->offmask ? "nexthdr+" : "", key->off);
 
 		if (key->offmask)
-			dp_dump(p, "[0x%u] ", key->offmask);
+			nl_dump(p, "[0x%u] ", key->offmask);
 
-		dp_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val));
+		nl_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val));
 
 		if (p->dp_type == NL_DUMP_STATS &&
 		    (u->cu_mask & U32_ATTR_PCNT)) {
 			struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data;
-			dp_dump(p, " successful %" PRIu64, pcnt->kcnts[i]);
+			nl_dump(p, " successful %" PRIu64, pcnt->kcnts[i]);
 		}
 	}
-
-	return line;
 }
 
-
-static int u32_dump_full(struct rtnl_cls *cls, struct nl_dump_params *p,
-			 int line)
+static void u32_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
 {
-	struct rtnl_u32 *u = u32_cls(cls);
+	struct rtnl_u32 *u = rtnl_cls_data(cls);
 	struct tc_u32_sel *s;
 
-	if (!u)
-		goto ignore;
-
 	if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
-		dp_dump(p, "no-selector\n");
-		return line;
+		nl_dump(p, "no-selector\n");
+		return;
 	}
 	
 	s = u->cu_selector->d_data;
 
-	dp_dump(p, "nkeys %u ", s->nkeys);
+	nl_dump(p, "nkeys %u ", s->nkeys);
 
 	if (u->cu_mask & U32_ATTR_HASH)
-		dp_dump(p, "ht key 0x%x hash 0x%u",
+		nl_dump(p, "ht key 0x%x hash 0x%u",
 			TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash));
 
 	if (u->cu_mask & U32_ATTR_LINK)
-		dp_dump(p, "link %u ", u->cu_link);
+		nl_dump(p, "link %u ", u->cu_link);
 
 	if (u->cu_mask & U32_ATTR_INDEV)
-		dp_dump(p, "indev %s ", u->cu_indev);
+		nl_dump(p, "indev %s ", u->cu_indev);
 
-	line = print_selector(p, s, cls, u, line);
-	dp_dump(p, "\n");
-
-ignore:
-	return line;
+	print_selector(p, s, cls, u);
+	nl_dump(p, "\n");
 
 #if 0	
 #define U32_ATTR_ACTION       0x040
@@ -340,64 +289,50 @@
 #endif
 }
 
-static int u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p,
-			  int line)
+static void u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p)
 {
-	struct rtnl_u32 *u = u32_cls(cls);
-
-	if (!u)
-		goto ignore;
+	struct rtnl_u32 *u = rtnl_cls_data(cls);
 
 	if (u->cu_mask & U32_ATTR_PCNT) {
 		struct tc_u32_pcnt *pc = u->cu_pcnt->d_data;
-		dp_dump(p, "\n");
-		dp_dump_line(p, line++, "%s         successful       hits\n");
-		dp_dump_line(p, line++, "%s           %8llu   %8llu\n",
+		nl_dump(p, "\n");
+		nl_dump_line(p, "    hit %8llu count %8llu\n",
 			     pc->rhit, pc->rcnt);
 	}
-
-ignore:
-	return line;
 }
 
-static struct nl_msg *u32_get_opts(struct rtnl_cls *cls)
+static int u32_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
 {
-	struct rtnl_u32 *u;
-	struct nl_msg *msg;
+	struct rtnl_u32 *u = rtnl_cls_data(cls);
 	
-	u = u32_cls(cls);
-	if (!u)
-		return NULL;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return NULL;
-
 	if (u->cu_mask & U32_ATTR_DIVISOR)
-		nla_put_u32(msg, TCA_U32_DIVISOR, u->cu_divisor);
+		NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor);
 
 	if (u->cu_mask & U32_ATTR_HASH)
-		nla_put_u32(msg, TCA_U32_HASH, u->cu_hash);
+		NLA_PUT_U32(msg, TCA_U32_HASH, u->cu_hash);
 
 	if (u->cu_mask & U32_ATTR_CLASSID)
-		nla_put_u32(msg, TCA_U32_CLASSID, u->cu_classid);
+		NLA_PUT_U32(msg, TCA_U32_CLASSID, u->cu_classid);
 
 	if (u->cu_mask & U32_ATTR_LINK)
-		nla_put_u32(msg, TCA_U32_LINK, u->cu_link);
+		NLA_PUT_U32(msg, TCA_U32_LINK, u->cu_link);
 
 	if (u->cu_mask & U32_ATTR_SELECTOR)
-		nla_put_data(msg, TCA_U32_SEL, u->cu_selector);
+		NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector);
 
 	if (u->cu_mask & U32_ATTR_ACTION)
-		nla_put_data(msg, TCA_U32_ACT, u->cu_act);
+		NLA_PUT_DATA(msg, TCA_U32_ACT, u->cu_act);
 
 	if (u->cu_mask & U32_ATTR_POLICE)
-		nla_put_data(msg, TCA_U32_POLICE, u->cu_police);
+		NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police);
 
 	if (u->cu_mask & U32_ATTR_INDEV)
-		nla_put_string(msg, TCA_U32_INDEV, u->cu_indev);
+		NLA_PUT_STRING(msg, TCA_U32_INDEV, u->cu_indev);
 
-	return msg;
+	return 0;
+
+nla_put_failure:
+	return -NLE_NOMEM;
 }
 
 /**
@@ -415,12 +350,8 @@
  
 int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
 {
-	struct rtnl_u32 *u;
+	struct rtnl_u32 *u = rtnl_cls_data(cls);
 	
-	u = u32_alloc(cls);
-	if (!u)
-		return nl_errno(ENOMEM);
-
 	u->cu_classid = classid;
 	u->cu_mask |= U32_ATTR_CLASSID;
 
@@ -437,15 +368,11 @@
 int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags)
 {
 	struct tc_u32_sel *sel;
-	struct rtnl_u32 *u;
-
-	u = u32_alloc(cls);
-	if (!u)
-		return nl_errno(ENOMEM);
+	struct rtnl_u32 *u = rtnl_cls_data(cls);
 
 	sel = u32_selector_alloc(u);
 	if (!sel)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	sel->flags |= flags;
 	u->cu_mask |= U32_ATTR_SELECTOR;
@@ -471,16 +398,12 @@
 		     int off, int offmask)
 {
 	struct tc_u32_sel *sel;
-	struct rtnl_u32 *u;
+	struct rtnl_u32 *u = rtnl_cls_data(cls);
 	int err;
 
-	u = u32_alloc(cls);
-	if (!u)
-		return nl_errno(ENOMEM);
-
 	sel = u32_selector_alloc(u);
 	if (!sel)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
 	if (err < 0)
@@ -523,7 +446,7 @@
 {
 	int shift = ((off & 3) == 0 ? 16 : 0);
 	if (off % 2)
-		return nl_error(EINVAL, "Invalid offset alignment");
+		return -NLE_INVAL;
 
 	return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift),
 				htonl((uint32_t)mask << shift),
@@ -580,13 +503,16 @@
 
 static struct rtnl_cls_ops u32_ops = {
 	.co_kind		= "u32",
+	.co_size		= sizeof(struct rtnl_u32),
 	.co_msg_parser		= u32_msg_parser,
 	.co_free_data		= u32_free_data,
 	.co_clone		= u32_clone,
 	.co_get_opts		= u32_get_opts,
-	.co_dump[NL_DUMP_BRIEF]	= u32_dump_brief,
-	.co_dump[NL_DUMP_FULL]	= u32_dump_full,
-	.co_dump[NL_DUMP_STATS]	= u32_dump_stats,
+	.co_dump = {
+	    [NL_DUMP_LINE]	= u32_dump_line,
+	    [NL_DUMP_DETAILS]	= u32_dump_details,
+	    [NL_DUMP_STATS]	= u32_dump_stats,
+	},
 };
 
 static void __init u32_init(void)
diff --git a/lib/route/cls_api.c b/lib/route/cls_api.c
index f5a083a..73f05df 100644
--- a/lib/route/cls_api.c
+++ b/lib/route/cls_api.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -44,7 +44,7 @@
 
 	for (op = &cls_ops_list; (o = *op) != NULL; op = &o->co_next)
 		if (!strcasecmp(cops->co_kind, o->co_kind))
-			return nl_errno(EEXIST);
+			return -NLE_EXIST;
 
 	cops->co_next = NULL;
 	*op = cops;
@@ -65,7 +65,7 @@
 			break;
 
 	if (!o)
-		return nl_errno(ENOENT);
+		return -NLE_OBJ_NOTFOUND;
 
 	*op = cops->co_next;
 
diff --git a/lib/route/cls_obj.c b/lib/route/cls_obj.c
index e12bc95..c8218c0 100644
--- a/lib/route/cls_obj.c
+++ b/lib/route/cls_obj.c
@@ -39,6 +39,8 @@
 	cops = rtnl_cls_lookup_ops(cls);
 	if (cops && cops->co_free_data)
 		cops->co_free_data(cls);
+
+	nl_data_free(cls->c_subdata);
 }
 
 static int cls_clone(struct nl_object *_dst, struct nl_object *_src)
@@ -52,6 +54,13 @@
 	if (err < 0)
 		goto errout;
 
+	if (src->c_subdata) {
+		if (!(dst->c_subdata = nl_data_clone(src->c_subdata))) {
+			err = -NLE_NOMEM;
+			goto errout;
+		}
+	}
+
 	cops = rtnl_cls_lookup_ops(src);
 	if (cops && cops->co_clone)
 		err = cops->co_clone(dst, src);
@@ -59,59 +68,50 @@
 	return err;
 }
 
-static int cls_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
+static void cls_dump_line(struct nl_object *obj, struct nl_dump_params *p)
 {
 	char buf[32];
 	struct rtnl_cls *cls = (struct rtnl_cls *) obj;
 	struct rtnl_cls_ops *cops;
-	int line;
 
-	line = tca_dump_brief((struct rtnl_tca *) cls, "cls", p, 0);
+	tca_dump_line((struct rtnl_tca *) cls, "cls", p);
 
-	dp_dump(p, " prio %u protocol %s", cls->c_prio,
+	nl_dump(p, " prio %u protocol %s", cls->c_prio,
 		nl_ether_proto2str(cls->c_protocol, buf, sizeof(buf)));
 
 	cops = rtnl_cls_lookup_ops(cls);
-	if (cops && cops->co_dump[NL_DUMP_BRIEF])
-		line = cops->co_dump[NL_DUMP_BRIEF](cls, p, line);
-	dp_dump(p, "\n");
-
-	return line;
+	if (cops && cops->co_dump[NL_DUMP_LINE])
+		cops->co_dump[NL_DUMP_LINE](cls, p);
+	nl_dump(p, "\n");
 }
 
-static int cls_dump_full(struct nl_object *obj, struct nl_dump_params *p)
+static void cls_dump_details(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_cls *cls = (struct rtnl_cls *) obj;
 	struct rtnl_cls_ops *cops;
-	int line;
 
-	line = cls_dump_brief(obj, p);
-	line = tca_dump_full((struct rtnl_tca *) cls, p, line);
+	cls_dump_line(obj, p);
+	tca_dump_details((struct rtnl_tca *) cls, p);
 
 	cops = rtnl_cls_lookup_ops(cls);
-	if (cops && cops->co_dump[NL_DUMP_FULL])
-		line = cops->co_dump[NL_DUMP_FULL](cls, p, line);
+	if (cops && cops->co_dump[NL_DUMP_DETAILS])
+		cops->co_dump[NL_DUMP_DETAILS](cls, p);
 	else
-		dp_dump(p, "no options\n");
-
-	return line;
+		nl_dump(p, "no options\n");
 }
 
-static int cls_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+static void cls_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_cls *cls = (struct rtnl_cls *) obj;
 	struct rtnl_cls_ops *cops;
-	int line;
 
-	line = cls_dump_full(obj, p);
-	line = tca_dump_stats((struct rtnl_tca *) cls, p, line);
-	dp_dump(p, "\n");
+	cls_dump_details(obj, p);
+	tca_dump_stats((struct rtnl_tca *) cls, p);
+	nl_dump(p, "\n");
 
 	cops = rtnl_cls_lookup_ops(cls);
 	if (cops && cops->co_dump[NL_DUMP_STATS])
-		line = cops->co_dump[NL_DUMP_STATS](cls, p, line);
-
-	return line;
+		cops->co_dump[NL_DUMP_STATS](cls, p);
 }
 
 /**
@@ -142,6 +142,11 @@
 	tca_set_ifindex((struct rtnl_tca *) f, ifindex);
 }
 
+int rtnl_cls_get_ifindex(struct rtnl_cls *cls)
+{
+	return cls->c_ifindex;
+}
+
 void rtnl_cls_set_handle(struct rtnl_cls *f, uint32_t handle)
 {
 	tca_set_handle((struct rtnl_tca *) f, handle);
@@ -152,19 +157,36 @@
 	tca_set_parent((struct rtnl_tca *) f, parent);
 }
 
-void rtnl_cls_set_kind(struct rtnl_cls *f, const char *kind)
+uint32_t rtnl_cls_get_parent(struct rtnl_cls *cls)
 {
-	tca_set_kind((struct rtnl_tca *) f, kind);
-	f->c_ops = __rtnl_cls_lookup_ops(kind);
+	return cls->c_parent;
 }
 
-void rtnl_cls_set_prio(struct rtnl_cls *cls, int prio)
+int rtnl_cls_set_kind(struct rtnl_cls *cls, const char *kind)
+{
+	if (cls->ce_mask & TCA_ATTR_KIND)
+		return -NLE_EXIST;
+
+	tca_set_kind((struct rtnl_tca *) cls, kind);
+
+	/* Force allocation of data */
+	rtnl_cls_data(cls);
+
+	return 0;
+}
+
+struct rtnl_cls_ops *rtnl_cls_get_ops(struct rtnl_cls *cls)
+{
+	return cls->c_ops;
+}
+
+void rtnl_cls_set_prio(struct rtnl_cls *cls, uint16_t prio)
 {
 	cls->c_prio = prio;
 	cls->ce_mask |= CLS_ATTR_PRIO;
 }
 
-int rtnl_cls_get_prio(struct rtnl_cls *cls)
+uint16_t rtnl_cls_get_prio(struct rtnl_cls *cls)
 {
 	if (cls->ce_mask & CLS_ATTR_PRIO)
 		return cls->c_prio;
@@ -172,13 +194,13 @@
 		return 0;
 }
 
-void rtnl_cls_set_protocol(struct rtnl_cls *cls, int protocol)
+void rtnl_cls_set_protocol(struct rtnl_cls *cls, uint16_t protocol)
 {
 	cls->c_protocol = protocol;
 	cls->ce_mask |= CLS_ATTR_PROTOCOL;
 }
 
-int rtnl_cls_get_protocol(struct rtnl_cls *cls)
+uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls)
 {
 	if (cls->ce_mask & CLS_ATTR_PROTOCOL)
 		return cls->c_protocol;
@@ -186,6 +208,32 @@
 		return ETH_P_ALL;
 }
 
+void *rtnl_cls_data(struct rtnl_cls *cls)
+{
+	if (!cls->c_subdata) {
+		struct rtnl_cls_ops *ops = cls->c_ops;
+
+		if (!ops) {
+			if (!cls->c_kind[0])
+				BUG();
+
+			ops = __rtnl_cls_lookup_ops(cls->c_kind);
+			if (ops == NULL)
+				return NULL;
+
+			cls->c_ops = ops;
+		}
+
+		if (!ops->co_size)
+			BUG();
+
+		if (!(cls->c_subdata = nl_data_alloc(NULL, ops->co_size)))
+			return NULL;
+	}
+
+	return nl_data_get(cls->c_subdata);
+}
+
 /** @} */
 
 struct nl_object_ops cls_obj_ops = {
@@ -193,9 +241,11 @@
 	.oo_size		= sizeof(struct rtnl_cls),
 	.oo_free_data		= cls_free_data,
 	.oo_clone		= cls_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= cls_dump_brief,
-	.oo_dump[NL_DUMP_FULL]	= cls_dump_full,
-	.oo_dump[NL_DUMP_STATS]	= cls_dump_stats,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= cls_dump_line,
+	    [NL_DUMP_DETAILS]	= cls_dump_details,
+	    [NL_DUMP_STATS]	= cls_dump_stats,
+	},
 	.oo_compare		= tca_compare,
 	.oo_id_attrs		= (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
 };
diff --git a/lib/route/link.c b/lib/route/link.c
index ab89c24..cf488e5 100644
--- a/lib/route/link.c
+++ b/lib/route/link.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -78,7 +78,7 @@
  * @code
  * // The first step is to retrieve a list of all available interfaces within
  * // the kernel and put them into a cache.
- * struct nl_cache *cache = rtnl_link_alloc_cache(nl_handle);
+ * struct nl_cache *cache = rtnl_link_alloc_cache(sk);
  *
  * // In a second step, a specific link may be looked up by either interface
  * // index or interface name.
@@ -112,12 +112,12 @@
  * // Two ways exist to commit this change request, the first one is to
  * // build the required netlink message and send it out in one single
  * // step:
- * rtnl_link_change(nl_handle, old, request);
+ * rtnl_link_change(sk, old, request);
  *
  * // An alternative way is to build the netlink message and send it
  * // out yourself using nl_send_auto_complete()
  * struct nl_msg *msg = rtnl_link_build_change_request(old, request);
- * nl_send_auto_complete(nl_handle, nlmsg_hdr(msg));
+ * nl_send_auto_complete(sk, nlmsg_hdr(msg));
  * nlmsg_free(msg);
  *
  * // Don't forget to give back the link object ;->
@@ -215,21 +215,19 @@
 
 	if (src->l_addr)
 		if (!(dst->l_addr = nl_addr_clone(src->l_addr)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	if (src->l_bcast)
 		if (!(dst->l_bcast = nl_addr_clone(src->l_bcast)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	if (src->l_info_ops && src->l_info_ops->io_clone) {
 		err = src->l_info_ops->io_clone(dst, src);
 		if (err < 0)
-			goto errout;
+			return err;
 	}
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
 static struct nla_policy link_policy[IFLA_MAX+1] = {
@@ -265,7 +263,7 @@
 
 	link = rtnl_link_alloc();
 	if (link == NULL) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 		
@@ -276,7 +274,7 @@
 		goto errout;
 
 	if (tb[IFLA_IFNAME] == NULL) {
-		err = nl_error(EINVAL, "Missing link name TLV");
+		err = -NLE_MISSING_ATTR;
 		goto errout;
 	}
 
@@ -332,18 +330,23 @@
 	}
 
 	if (tb[IFLA_ADDRESS]) {
-		link->l_addr = nla_get_addr(tb[IFLA_ADDRESS], AF_UNSPEC);
-		if (link->l_addr == NULL)
+		link->l_addr = nl_addr_alloc_attr(tb[IFLA_ADDRESS], AF_UNSPEC);
+		if (link->l_addr == NULL) {
+			err = -NLE_NOMEM;
 			goto errout;
+		}
 		nl_addr_set_family(link->l_addr,
 				   nl_addr_guess_family(link->l_addr));
 		link->ce_mask |= LINK_ATTR_ADDR;
 	}
 
 	if (tb[IFLA_BROADCAST]) {
-		link->l_bcast = nla_get_addr(tb[IFLA_BROADCAST], AF_UNSPEC);
-		if (link->l_bcast == NULL)
+		link->l_bcast = nl_addr_alloc_attr(tb[IFLA_BROADCAST],
+						   AF_UNSPEC);
+		if (link->l_bcast == NULL) {
+			err = -NLE_NOMEM;
 			goto errout;
+		}
 		nl_addr_set_family(link->l_bcast,
 				   nl_addr_guess_family(link->l_bcast));
 		link->ce_mask |= LINK_ATTR_BRD;
@@ -365,13 +368,8 @@
 	}
 
 	if (tb[IFLA_MAP]) {
-		struct rtnl_link_ifmap *map =  nla_data(tb[IFLA_MAP]);
-		link->l_map.lm_mem_start = map->mem_start;
-		link->l_map.lm_mem_end   = map->mem_end;
-		link->l_map.lm_base_addr = map->base_addr;
-		link->l_map.lm_irq       = map->irq;
-		link->l_map.lm_dma       = map->dma;
-		link->l_map.lm_port      = map->port;
+		nla_memcpy(&link->l_map, tb[IFLA_MAP], 
+			   sizeof(struct rtnl_link_ifmap));
 		link->ce_mask |= LINK_ATTR_MAP;
 	}
 
@@ -419,125 +417,109 @@
 	}
 
 	err = pp->pp_cb((struct nl_object *) link, pp);
-	if (err < 0)
-		goto errout;
-
-	err = P_ACCEPT;
-
 errout:
 	rtnl_link_put(link);
 	return err;
 }
 
-static int link_request_update(struct nl_cache *c, struct nl_handle *h)
+static int link_request_update(struct nl_cache *cache, struct nl_sock *sk)
 {
-	return nl_rtgen_request(h, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP);
+	return nl_rtgen_request(sk, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP);
 }
 
-static int link_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
+static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p)
 {
 	char buf[128];
 	struct nl_cache *cache = dp_cache(obj);
 	struct rtnl_link *link = (struct rtnl_link *) obj;
-	int line = 1;
 
-	dp_dump(p, "%s %s ", link->l_name,
-			     nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
+	nl_dump_line(p, "%s %s ", link->l_name,
+		     nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
 
 	if (link->l_addr && !nl_addr_iszero(link->l_addr))
-		dp_dump(p, "%s ", nl_addr2str(link->l_addr, buf, sizeof(buf)));
+		nl_dump(p, "%s ", nl_addr2str(link->l_addr, buf, sizeof(buf)));
 
 	if (link->ce_mask & LINK_ATTR_MASTER) {
 		struct rtnl_link *master = rtnl_link_get(cache, link->l_master);
-		dp_dump(p, "master %s ", master ? master->l_name : "inv");
+		nl_dump(p, "master %s ", master ? master->l_name : "inv");
 		if (master)
 			rtnl_link_put(master);
 	}
 
 	rtnl_link_flags2str(link->l_flags, buf, sizeof(buf));
 	if (buf[0])
-		dp_dump(p, "<%s> ", buf);
+		nl_dump(p, "<%s> ", buf);
 
 	if (link->ce_mask & LINK_ATTR_LINK) {
 		struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
-		dp_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE");
+		nl_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE");
 		if (ll)
 			rtnl_link_put(ll);
 	}
 
-	if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_BRIEF])
-		line = link->l_info_ops->io_dump[NL_DUMP_BRIEF](link, p, line);
+	if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_LINE])
+		link->l_info_ops->io_dump[NL_DUMP_LINE](link, p);
 
-	dp_dump(p, "\n");
-
-	return line;
+	nl_dump(p, "\n");
 }
 
-static int link_dump_full(struct nl_object *obj, struct nl_dump_params *p)
+static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_link *link = (struct rtnl_link *) obj;
 	char buf[64];
-	int line;
 
-	line = link_dump_brief(obj, p);
-	dp_new_line(p, line++);
+	link_dump_line(obj, p);
 
-	dp_dump(p, "    mtu %u ", link->l_mtu);
-	dp_dump(p, "txqlen %u weight %u ", link->l_txqlen, link->l_weight);
+	nl_dump_line(p, "    mtu %u ", link->l_mtu);
+	nl_dump(p, "txqlen %u weight %u ", link->l_txqlen, link->l_weight);
 
 	if (link->ce_mask & LINK_ATTR_QDISC)
-		dp_dump(p, "qdisc %s ", link->l_qdisc);
+		nl_dump(p, "qdisc %s ", link->l_qdisc);
 
 	if (link->ce_mask & LINK_ATTR_MAP && link->l_map.lm_irq)
-		dp_dump(p, "irq %u ", link->l_map.lm_irq);
+		nl_dump(p, "irq %u ", link->l_map.lm_irq);
 
 	if (link->ce_mask & LINK_ATTR_IFINDEX)
-		dp_dump(p, "index %u ", link->l_index);
+		nl_dump(p, "index %u ", link->l_index);
 
 
-	dp_dump(p, "\n");
-	dp_new_line(p, line++);
-
-	dp_dump(p, "    ");
+	nl_dump(p, "\n");
+	nl_dump_line(p, "    ");
 
 	if (link->ce_mask & LINK_ATTR_BRD)
-		dp_dump(p, "brd %s ", nl_addr2str(link->l_bcast, buf,
+		nl_dump(p, "brd %s ", nl_addr2str(link->l_bcast, buf,
 						   sizeof(buf)));
 
 	if ((link->ce_mask & LINK_ATTR_OPERSTATE) &&
 	    link->l_operstate != IF_OPER_UNKNOWN) {
 		rtnl_link_operstate2str(link->l_operstate, buf, sizeof(buf));
-		dp_dump(p, "state %s ", buf);
+		nl_dump(p, "state %s ", buf);
 	}
 
-	dp_dump(p, "mode %s\n",
+	nl_dump(p, "mode %s\n",
 		rtnl_link_mode2str(link->l_linkmode, buf, sizeof(buf)));
 
-	if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_FULL])
-		line = link->l_info_ops->io_dump[NL_DUMP_FULL](link, p, line);
-
-	return line;
+	if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_DETAILS])
+		link->l_info_ops->io_dump[NL_DUMP_DETAILS](link, p);
 }
 
-static int link_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_link *link = (struct rtnl_link *) obj;
 	char *unit, fmt[64];
 	float res;
-	int line;
 	
-	line = link_dump_full(obj, p);
+	link_dump_details(obj, p);
 
-	dp_dump_line(p, line++, "    Stats:    bytes    packets     errors "
-				"   dropped   fifo-err compressed\n");
+	nl_dump_line(p, "    Stats:    bytes    packets     errors "
+			"   dropped   fifo-err compressed\n");
 
 	res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_RX_BYTES], &unit);
 
-	strcpy(fmt, "    RX  %X.2f %s %10llu %10llu %10llu %10llu %10llu\n");
+	strcpy(fmt, "     RX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n");
 	fmt[9] = *unit == 'B' ? '9' : '7';
 	
-	dp_dump_line(p, line++, fmt,
-		res, unit,
+	nl_dump_line(p, fmt, res, unit,
 		link->l_stats[RTNL_LINK_RX_PACKETS],
 		link->l_stats[RTNL_LINK_RX_ERRORS],
 		link->l_stats[RTNL_LINK_RX_DROPPED],
@@ -546,21 +528,20 @@
 
 	res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_TX_BYTES], &unit);
 
-	strcpy(fmt, "    TX  %X.2f %s %10llu %10llu %10llu %10llu %10llu\n");
+	strcpy(fmt, "     TX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n");
 	fmt[9] = *unit == 'B' ? '9' : '7';
 	
-	dp_dump_line(p, line++, fmt,
-		res, unit,
+	nl_dump_line(p, fmt, res, unit,
 		link->l_stats[RTNL_LINK_TX_PACKETS],
 		link->l_stats[RTNL_LINK_TX_ERRORS],
 		link->l_stats[RTNL_LINK_TX_DROPPED],
 		link->l_stats[RTNL_LINK_TX_FIFO_ERR],
 		link->l_stats[RTNL_LINK_TX_COMPRESSED]);
 
-	dp_dump_line(p, line++, "    Errors:  length       over        crc "
-				"     frame     missed  multicast\n");
+	nl_dump_line(p, "    Errors:  length       over        crc "
+			"     frame     missed  multicast\n");
 
-	dp_dump_line(p, line++, "    RX   %10" PRIu64 " %10" PRIu64 " %10"
+	nl_dump_line(p, "     RX  %10" PRIu64 " %10" PRIu64 " %10"
 				PRIu64 " %10" PRIu64 " %10" PRIu64 " %10"
 				PRIu64 "\n",
 		link->l_stats[RTNL_LINK_RX_LEN_ERR],
@@ -570,11 +551,11 @@
 		link->l_stats[RTNL_LINK_RX_MISSED_ERR],
 		link->l_stats[RTNL_LINK_MULTICAST]);
 
-	dp_dump_line(p, line++, "    Errors: aborted    carrier  heartbeat "
-				"    window  collision\n");
+	nl_dump_line(p, "            aborted    carrier  heartbeat "
+			"    window  collision\n");
 	
-	dp_dump_line(p, line++, "    TX   %10" PRIu64 " %10" PRIu64 " %10"
-				PRIu64 " %10" PRIu64 " %10" PRIu64 "\n",
+	nl_dump_line(p, "     TX  %10" PRIu64 " %10" PRIu64 " %10"
+			PRIu64 " %10" PRIu64 " %10" PRIu64 "\n",
 		link->l_stats[RTNL_LINK_TX_ABORT_ERR],
 		link->l_stats[RTNL_LINK_TX_CARRIER_ERR],
 		link->l_stats[RTNL_LINK_TX_HBEAT_ERR],
@@ -582,132 +563,56 @@
 		link->l_stats[RTNL_LINK_TX_COLLISIONS]);
 
 	if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_STATS])
-		line = link->l_info_ops->io_dump[NL_DUMP_STATS](link, p, line);
-
-	return line;
+		link->l_info_ops->io_dump[NL_DUMP_STATS](link, p);
 }
 
-static int link_dump_xml(struct nl_object *obj, struct nl_dump_params *p)
+static void link_dump_env(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_link *link = (struct rtnl_link *) obj;
 	struct nl_cache *cache = dp_cache(obj);
 	char buf[128];
-	int i, line = 0;
+	int i;
 
-	dp_dump_line(p, line++, "<link name=\"%s\" index=\"%u\">\n",
-		     link->l_name, link->l_index);
-	dp_dump_line(p, line++, "  <family>%s</family>\n",
+	nl_dump_line(p, "LINK_NAME=%s\n", link->l_name);
+	nl_dump_line(p, "LINK_IFINDEX=%u\n", link->l_index);
+	nl_dump_line(p, "LINK_FAMILY=%s\n",
 		     nl_af2str(link->l_family, buf, sizeof(buf)));
-	dp_dump_line(p, line++, "  <arptype>%s</arptype>\n",
-		     nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
-	dp_dump_line(p, line++, "  <address>%s</address>\n",
-		     nl_addr2str(link->l_addr, buf, sizeof(buf)));
-	dp_dump_line(p, line++, "  <mtu>%u</mtu>\n", link->l_mtu);
-	dp_dump_line(p, line++, "  <txqlen>%u</txqlen>\n", link->l_txqlen);
-	dp_dump_line(p, line++, "  <weight>%u</weight>\n", link->l_weight);
-
-	rtnl_link_flags2str(link->l_flags, buf, sizeof(buf));
-	if (buf[0])
-		dp_dump_line(p, line++, "  <flags>%s</flags>\n", buf);
-
-	if (link->ce_mask & LINK_ATTR_QDISC)
-		dp_dump_line(p, line++, "  <qdisc>%s</qdisc>\n", link->l_qdisc);
-
-	if (link->ce_mask & LINK_ATTR_LINK) {
-		struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
-		dp_dump_line(p, line++, "  <link>%s</link>\n",
-			     ll ? ll->l_name : "none");
-		if (ll)
-			rtnl_link_put(ll);
-	}
-
-	if (link->ce_mask & LINK_ATTR_MASTER) {
-		struct rtnl_link *master = rtnl_link_get(cache, link->l_master);
-		dp_dump_line(p, line++, "  <master>%s</master>\n",
-			     master ? master->l_name : "none");
-		if (master)
-			rtnl_link_put(master);
-	}
-
-	if (link->ce_mask & LINK_ATTR_BRD)
-		dp_dump_line(p, line++, "  <broadcast>%s</broadcast>\n",
-			     nl_addr2str(link->l_bcast, buf, sizeof(buf)));
-
-	if (link->ce_mask & LINK_ATTR_STATS) {
-		dp_dump_line(p, line++, "  <stats>\n");
-		for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) {
-			rtnl_link_stat2str(i, buf, sizeof(buf));
-			dp_dump_line(p, line++,
-				     "    <%s>%" PRIu64 "</%s>\n",
-				     buf, link->l_stats[i], buf);
-		}
-		dp_dump_line(p, line++, "  </stats>\n");
-	}
-
-	if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_XML]) {
-		dp_dump_line(p, line++, "  <info>\n");
-		line = link->l_info_ops->io_dump[NL_DUMP_XML](link, p, line);
-		dp_dump_line(p, line++, "  </info>\n");
-	}
-
-	dp_dump_line(p, line++, "</link>\n");
-
-#if 0
-	uint32_t	l_change;	/**< Change mask */
-	struct rtnl_lifmap l_map;	/**< Interface device mapping */
-#endif
-
-	return line;
-}
-
-static int link_dump_env(struct nl_object *obj, struct nl_dump_params *p)
-{
-	struct rtnl_link *link = (struct rtnl_link *) obj;
-	struct nl_cache *cache = dp_cache(obj);
-	char buf[128];
-	int i, line = 0;
-
-	dp_dump_line(p, line++, "LINK_NAME=%s\n", link->l_name);
-	dp_dump_line(p, line++, "LINK_IFINDEX=%u\n", link->l_index);
-	dp_dump_line(p, line++, "LINK_FAMILY=%s\n",
-		     nl_af2str(link->l_family, buf, sizeof(buf)));
-	dp_dump_line(p, line++, "LINK_TYPE=%s\n",
+	nl_dump_line(p, "LINK_TYPE=%s\n",
 		     nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
 	if (link->ce_mask & LINK_ATTR_ADDR)
-		dp_dump_line(p, line++, "LINK_ADDRESS=%s\n",
+		nl_dump_line(p, "LINK_ADDRESS=%s\n",
 			     nl_addr2str(link->l_addr, buf, sizeof(buf)));
-	dp_dump_line(p, line++, "LINK_MTU=%u\n", link->l_mtu);
-	dp_dump_line(p, line++, "LINK_TXQUEUELEN=%u\n", link->l_txqlen);
-	dp_dump_line(p, line++, "LINK_WEIGHT=%u\n", link->l_weight);
+	nl_dump_line(p, "LINK_MTU=%u\n", link->l_mtu);
+	nl_dump_line(p, "LINK_TXQUEUELEN=%u\n", link->l_txqlen);
+	nl_dump_line(p, "LINK_WEIGHT=%u\n", link->l_weight);
 
 	rtnl_link_flags2str(link->l_flags & ~IFF_RUNNING, buf, sizeof(buf));
 	if (buf[0])
-		dp_dump_line(p, line++, "LINK_FLAGS=%s\n", buf);
+		nl_dump_line(p, "LINK_FLAGS=%s\n", buf);
 
 	if (link->ce_mask & LINK_ATTR_QDISC)
-		dp_dump_line(p, line++, "LINK_QDISC=%s\n", link->l_qdisc);
+		nl_dump_line(p, "LINK_QDISC=%s\n", link->l_qdisc);
 
 	if (link->ce_mask & LINK_ATTR_LINK) {
 		struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
 
-		dp_dump_line(p, line++, "LINK_LINK_IFINDEX=%d\n", link->l_link);
+		nl_dump_line(p, "LINK_LINK_IFINDEX=%d\n", link->l_link);
 		if (ll) {
-			dp_dump_line(p, line++, "LINK_LINK_IFNAME=%s\n",
-				     ll->l_name);
+			nl_dump_line(p, "LINK_LINK_IFNAME=%s\n", ll->l_name);
 			rtnl_link_put(ll);
 		}
 	}
 
 	if (link->ce_mask & LINK_ATTR_MASTER) {
 		struct rtnl_link *master = rtnl_link_get(cache, link->l_master);
-		dp_dump_line(p, line++, "LINK_MASTER=%s\n",
+		nl_dump_line(p, "LINK_MASTER=%s\n",
 			     master ? master->l_name : "none");
 		if (master)
 			rtnl_link_put(master);
 	}
 
 	if (link->ce_mask & LINK_ATTR_BRD)
-		dp_dump_line(p, line++, "LINK_BROADCAST=%s\n",
+		nl_dump_line(p, "LINK_BROADCAST=%s\n",
 			     nl_addr2str(link->l_bcast, buf, sizeof(buf)));
 
 	if (link->ce_mask & LINK_ATTR_STATS) {
@@ -720,15 +625,12 @@
 				*c = toupper(*c);
 				c++;
 			}
-			dp_dump_line(p, line++,
-				     "%s=%" PRIu64 "\n", buf, link->l_stats[i]);
+			nl_dump_line(p, "%s=%" PRIu64 "\n", buf, link->l_stats[i]);
 		}
 	}
 
 	if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_ENV])
-		line = link->l_info_ops->io_dump[NL_DUMP_ENV](link, p, line);
-
-	return line;
+		link->l_info_ops->io_dump[NL_DUMP_ENV](link, p);
 }
 
 #if 0
@@ -800,7 +702,7 @@
 	diff |= LINK_DIFF(ADDR,		nl_addr_cmp(a->l_addr, b->l_addr));
 	diff |= LINK_DIFF(BRD,		nl_addr_cmp(a->l_bcast, b->l_bcast));
 
-	if (flags & LOOSE_FLAG_COMPARISON)
+	if (flags & LOOSE_COMPARISON)
 		diff |= LINK_DIFF(FLAGS,
 				  (a->l_flags ^ b->l_flags) & b->l_flag_mask);
 	else
@@ -863,28 +765,17 @@
 
 /**
  * Allocate link cache and fill in all configured links.
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
+ * @arg result		Pointer to store resulting cache.
  *
  * Allocates a new link cache, initializes it properly and updates it
  * to include all links currently configured in the kernel.
  *
- * @note Free the memory after usage.
- * @return Newly allocated cache or NULL if an error occured.
+ * @return 0 on success or a negative error code.
  */
-struct nl_cache *rtnl_link_alloc_cache(struct nl_handle *handle)
+int rtnl_link_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
 {
-	struct nl_cache * cache;
-	
-	cache = nl_cache_alloc(&rtnl_link_ops);
-	if (cache == NULL)
-		return NULL;
-	
-	if (handle && nl_cache_refill(handle, cache) < 0) {
-		nl_cache_free(cache);
-		return NULL;
-	}
-
-	return cache;
+	return nl_cache_alloc_and_fill(&rtnl_link_ops, sk, result);
 }
 
 /**
@@ -967,9 +858,9 @@
  * @note Not all attributes can be changed, see
  *       \ref link_changeable "Changeable Attributes" for more details.
  */
-struct nl_msg * rtnl_link_build_change_request(struct rtnl_link *old,
-					       struct rtnl_link *tmpl,
-					       int flags)
+int rtnl_link_build_change_request(struct rtnl_link *old,
+				   struct rtnl_link *tmpl, int flags,
+				   struct nl_msg **result)
 {
 	struct nl_msg *msg;
 	struct ifinfomsg ifi = {
@@ -984,7 +875,7 @@
 
 	msg = nlmsg_alloc_simple(RTM_SETLINK, flags);
 	if (!msg)
-		goto nla_put_failure;
+		return -NLE_NOMEM;
 
 	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
 		goto nla_put_failure;
@@ -1028,16 +919,17 @@
 		nla_nest_end(msg, info);
 	}
 
-	return msg;
+	*result = msg;
+	return 0;
 
 nla_put_failure:
 	nlmsg_free(msg);
-	return NULL;
+	return -NLE_MSGSIZE;
 }
 
 /**
  * Change link attributes
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg old		link to be changed
  * @arg tmpl		template with requested changes
  * @arg flags		additional netlink message flags
@@ -1050,22 +942,21 @@
  * @note Not all attributes can be changed, see
  *       \ref link_changeable "Changeable Attributes" for more details.
  */
-int rtnl_link_change(struct nl_handle *handle, struct rtnl_link *old,
+int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *old,
 		     struct rtnl_link *tmpl, int flags)
 {
-	int err;
 	struct nl_msg *msg;
+	int err;
 	
-	msg = rtnl_link_build_change_request(old, tmpl, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_link_build_change_request(old, tmpl, flags, &msg)) < 0)
+		return err;
 	
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -1106,11 +997,11 @@
  * @arg cache		link cache
  * @arg name		link name
  *
- * @return interface index or RTNL_LINK_NOT_FOUND if no match was found.
+ * @return interface index or 0 if no match was found.
  */
 int rtnl_link_name2i(struct nl_cache *cache, const char *name)
 {
-	int ifindex = RTNL_LINK_NOT_FOUND;
+	int ifindex = 0;
 	struct rtnl_link *link;
 	
 	link = rtnl_link_get_by_name(cache, name);
@@ -1380,10 +1271,7 @@
 
 int rtnl_link_get_ifindex(struct rtnl_link *link)
 {
-	if (link->ce_mask & LINK_ATTR_IFINDEX)
-		return link->l_index;
-	else
-		return RTNL_LINK_NOT_FOUND;
+	return link->l_index;
 }
 
 void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu)
@@ -1436,10 +1324,7 @@
 
 int rtnl_link_get_link(struct rtnl_link *link)
 {
-	if (link->ce_mask & LINK_ATTR_LINK)
-		return link->l_link;
-	else
-		return RTNL_LINK_NOT_FOUND;
+	return link->l_link;
 }
 
 void rtnl_link_set_master(struct rtnl_link *link, int ifindex)
@@ -1450,10 +1335,7 @@
 
 int rtnl_link_get_master(struct rtnl_link *link)
 {
-	if (link->ce_mask & LINK_ATTR_MASTER)
-		return link->l_master;
-	else
-		return RTNL_LINK_NOT_FOUND;
+	return link->l_master;
 }
 
 void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t operstate)
@@ -1509,7 +1391,7 @@
 	int err;
 
 	if ((io = rtnl_link_info_ops_lookup(type)) == NULL)
-		return nl_error(ENOENT, "No such link info type exists");
+		return -NLE_OPNOTSUPP;
 
 	if (link->l_info_ops)
 		release_link_info(link);
@@ -1544,11 +1426,12 @@
 	.oo_size		= sizeof(struct rtnl_link),
 	.oo_free_data		= link_free_data,
 	.oo_clone		= link_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= link_dump_brief,
-	.oo_dump[NL_DUMP_FULL]	= link_dump_full,
-	.oo_dump[NL_DUMP_STATS]	= link_dump_stats,
-	.oo_dump[NL_DUMP_XML]	= link_dump_xml,
-	.oo_dump[NL_DUMP_ENV]	= link_dump_env,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= link_dump_line,
+	    [NL_DUMP_DETAILS]	= link_dump_details,
+	    [NL_DUMP_STATS]	= link_dump_stats,
+	    [NL_DUMP_ENV]	= link_dump_env,
+	},
 	.oo_compare		= link_compare,
 	.oo_attrs2str		= link_attrs2str,
 	.oo_id_attrs		= LINK_ATTR_IFINDEX,
diff --git a/lib/route/link/api.c b/lib/route/link/api.c
index afe00b1..a0e7679 100644
--- a/lib/route/link/api.c
+++ b/lib/route/link/api.c
@@ -61,10 +61,10 @@
 int rtnl_link_register_info(struct rtnl_link_info_ops *ops)
 {
 	if (ops->io_name == NULL)
-		return nl_error(EINVAL, "No name specified");
+		return -NLE_INVAL;
 
 	if (rtnl_link_info_ops_lookup(ops->io_name))
-		return nl_error(EEXIST, "Link info operations already exist");
+		return -NLE_EXIST;
 
 	NL_DBG(1, "Registered link info operations %s\n", ops->io_name);
 
@@ -83,10 +83,10 @@
 			break;
 
 	if (!t)
-		return nl_error(ENOENT, "No such link info operations");
+		return -NLE_OPNOTSUPP;
 
 	if (t->io_refcnt > 0)
-		return nl_error(EBUSY, "Info operations in use");
+		return -NLE_BUSY;
 
 	NL_DBG(1, "Unregistered link info perations %s\n", ops->io_name);
 
diff --git a/lib/route/link/vlan.c b/lib/route/link/vlan.c
index c148dca..c466afe 100644
--- a/lib/route/link/vlan.c
+++ b/lib/route/link/vlan.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2007 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -73,7 +73,7 @@
 	struct vlan_info *vi;
 
 	if ((vi = calloc(1, sizeof(*vi))) == NULL)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	link->l_info = vi;
 
@@ -119,12 +119,11 @@
 
 		nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) {
 			if (nla_len(nla) < sizeof(*map))
-				return nl_error(EINVAL, "Malformed mapping");
+				return -NLE_INVAL;
 
 			map = nla_data(nla);
 			if (map->from < 0 || map->from > VLAN_PRIO_MAX) {
-				return nl_error(EINVAL, "VLAN prio %d out of "
-						"range", map->from);
+				return -NLE_INVAL;
 			}
 
 			vi->vi_ingress_qos[map->from] = map->to;
@@ -140,7 +139,7 @@
 
 		nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
 			if (nla_len(nla) < sizeof(*map))
-				return nl_error(EINVAL, "Malformed mapping");
+				return -NLE_INVAL;
 			i++;
 		}
 
@@ -148,7 +147,7 @@
 		vi->vi_egress_size = (i + 32) & ~31;
 		vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map));
 		if (vi->vi_egress_qos == NULL)
-			return nl_errno(ENOMEM);
+			return -NLE_NOMEM;
 
 		i = 0;
 		nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
@@ -180,71 +179,60 @@
 	link->l_info = NULL;
 }
 
-static int vlan_dump_brief(struct rtnl_link *link, struct nl_dump_params *p,
-			   int line)
+static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
 {
 	struct vlan_info *vi = link->l_info;
 
-	dp_dump(p, "vlan-id %d", vi->vi_vlan_id);
-
-	return line;
+	nl_dump(p, "vlan-id %d", vi->vi_vlan_id);
 }
 
-static int vlan_dump_full(struct rtnl_link *link, struct nl_dump_params *p,
-			  int line)
+static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
 {
 	struct vlan_info *vi = link->l_info;
 	int i, printed;
 	char buf[64];
 
 	rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
-	dp_dump_line(p, line++, "    vlan-info id %d <%s>\n",
-		vi->vi_vlan_id, buf);
+	nl_dump_line(p, "    vlan-info id %d <%s>\n", vi->vi_vlan_id, buf);
 
 	if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
-		dp_dump_line(p, line++,
+		nl_dump_line(p, 
 		"      ingress vlan prio -> qos/socket prio mapping:\n");
 		for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) {
 			if (vi->vi_ingress_qos[i]) {
-				if (printed == 0) {
-					dp_new_line(p, line);
-					dp_dump(p, "      ");
-				}
-				dp_dump(p, "%x -> %#08x, ",
+				if (printed == 0)
+					nl_dump_line(p, "      ");
+				nl_dump(p, "%x -> %#08x, ",
 					i, vi->vi_ingress_qos[i]);
 				if (printed++ == 3) {
-					dp_dump(p, "\n");
+					nl_dump(p, "\n");
 					printed = 0;
 				}
 			}
 		}
 
 		if (printed > 0 && printed != 4)
-			dp_dump(p, "\n");
+			nl_dump(p, "\n");
 	}
 
 	if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
-		dp_dump_line(p, line++,
+		nl_dump_line(p, 
 		"      egress qos/socket prio -> vlan prio mapping:\n");
 		for (i = 0, printed = 0; i < vi->vi_negress; i++) {
-			if (printed == 0) {
-				dp_new_line(p, line);
-				dp_dump(p, "      ");
-			}
-			dp_dump(p, "%#08x -> %x, ",
+			if (printed == 0)
+				nl_dump_line(p, "      ");
+			nl_dump(p, "%#08x -> %x, ",
 				vi->vi_egress_qos[i].vm_from,
 				vi->vi_egress_qos[i].vm_to);
 			if (printed++ == 3) {
-				dp_dump(p, "\n");
+				nl_dump(p, "\n");
 				printed = 0;
 			}
 		}
 
 		if (printed > 0 && printed != 4)
-			dp_dump(p, "\n");
+			nl_dump(p, "\n");
 	}
-
-	return line;
 }
 
 static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
@@ -260,7 +248,7 @@
 	vdst->vi_egress_qos = calloc(vsrc->vi_egress_size,
 				     sizeof(struct vlan_map));
 	if (!vdst->vi_egress_qos)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
 	       vsrc->vi_egress_size * sizeof(struct vlan_map));
@@ -274,7 +262,7 @@
 	struct nlattr *data;
 
 	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
-		return nl_errno(ENOBUFS);
+		return -NLE_MSGSIZE;
 
 	if (vi->vi_mask & VLAN_HAS_ID)
 		NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
@@ -337,8 +325,10 @@
 	.io_name		= "vlan",
 	.io_alloc		= vlan_alloc,
 	.io_parse		= vlan_parse,
-	.io_dump[NL_DUMP_BRIEF]	= vlan_dump_brief,
-	.io_dump[NL_DUMP_FULL]	= vlan_dump_full,
+	.io_dump = {
+	    [NL_DUMP_LINE]	= vlan_dump_line,
+	    [NL_DUMP_DETAILS]	= vlan_dump_details,
+	},
 	.io_clone		= vlan_clone,
 	.io_put_attrs		= vlan_put_attrs,
 	.io_free		= vlan_free,
@@ -349,7 +339,7 @@
 	struct vlan_info *vi = link->l_info;
 
 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
-		return nl_error(EOPNOTSUPP, "Not a VLAN link");
+		return -NLE_OPNOTSUPP;
 
 	vi->vi_vlan_id = id;
 	vi->vi_mask |= VLAN_HAS_ID;
@@ -362,7 +352,7 @@
 	struct vlan_info *vi = link->l_info;
 
 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
-		return nl_error(EOPNOTSUPP, "Not a VLAN link");
+		return -NLE_OPNOTSUPP;
 
 	if (vi->vi_mask & VLAN_HAS_ID)
 		return vi->vi_vlan_id;
@@ -375,7 +365,7 @@
 	struct vlan_info *vi = link->l_info;
 
 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
-		return nl_error(EOPNOTSUPP, "Not a VLAN link");
+		return -NLE_OPNOTSUPP;
 
 	vi->vi_flags_mask |= flags;
 	vi->vi_flags |= flags;
@@ -389,7 +379,7 @@
 	struct vlan_info *vi = link->l_info;
 
 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
-		return nl_error(EOPNOTSUPP, "Not a VLAN link");
+		return -NLE_OPNOTSUPP;
 
 	vi->vi_flags_mask |= flags;
 	vi->vi_flags &= ~flags;
@@ -403,7 +393,7 @@
 	struct vlan_info *vi = link->l_info;
 
 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
-		return nl_error(EOPNOTSUPP, "Not a VLAN link");
+		return -NLE_OPNOTSUPP;
 
 	return vi->vi_flags;
 }
@@ -414,11 +404,10 @@
 	struct vlan_info *vi = link->l_info;
 
 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
-		return nl_error(EOPNOTSUPP, "Not a VLAN link");
+		return -NLE_OPNOTSUPP;
 
 	if (from < 0 || from > VLAN_PRIO_MAX)
-		return nl_error(EINVAL, "Invalid vlan prio 0..%d",
-			VLAN_PRIO_MAX);
+		return -NLE_INVAL;
 
 	vi->vi_ingress_qos[from] = to;
 	vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
@@ -430,10 +419,8 @@
 {
 	struct vlan_info *vi = link->l_info;
 
-	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) {
-		nl_error(EOPNOTSUPP, "Not a VLAN link");
+	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
 		return NULL;
-	}
 
 	if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
 		return vi->vi_ingress_qos;
@@ -446,11 +433,10 @@
 	struct vlan_info *vi = link->l_info;
 
 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
-		return nl_error(EOPNOTSUPP, "Not a VLAN link");
+		return -NLE_OPNOTSUPP;
 
 	if (to < 0 || to > VLAN_PRIO_MAX)
-		return nl_error(EINVAL, "Invalid vlan prio 0..%d",
-			VLAN_PRIO_MAX);
+		return -NLE_INVAL;
 
 	if (vi->vi_negress >= vi->vi_egress_size) {
 		int new_size = vi->vi_egress_size + 32;
@@ -458,7 +444,7 @@
 
 		ptr = realloc(vi->vi_egress_qos, new_size);
 		if (!ptr)
-			return nl_errno(ENOMEM);
+			return -NLE_NOMEM;
 
 		vi->vi_egress_qos = ptr;
 		vi->vi_egress_size = new_size;
@@ -477,15 +463,11 @@
 {
 	struct vlan_info *vi = link->l_info;
 
-	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) {
-		nl_error(EOPNOTSUPP, "Not a VLAN link");
+	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
 		return NULL;
-	}
 
-	if (negress == NULL) {
-		nl_error(EINVAL, "Require pointer to store negress");
+	if (negress == NULL)
 		return NULL;
-	}
 
 	if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
 		*negress = vi->vi_negress;
diff --git a/lib/route/neigh.c b/lib/route/neigh.c
index 6f2f0d3..d4dc82c 100644
--- a/lib/route/neigh.c
+++ b/lib/route/neigh.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -61,7 +61,7 @@
  * @code
  * // The first step is to retrieve a list of all available neighbour within
  * // the kernel and put them into a cache.
- * struct nl_cache *cache = rtnl_neigh_alloc_cache(handle);
+ * struct nl_cache *cache = rtnl_neigh_alloc_cache(sk);
  *
  * // Neighbours can then be looked up by the interface and destination
  * // address:
@@ -86,7 +86,7 @@
  * // block until the operation has been completed. Alternatively the required
  * // netlink message can be built using rtnl_neigh_build_add_request()
  * // to be sent out using nl_send_auto_complete().
- * rtnl_neigh_add(nl_handle, neigh, NLM_F_REPLACE);
+ * rtnl_neigh_add(sk, neigh, NLM_F_CREATE);
  *
  * // Free the memory
  * rtnl_neigh_put(neigh);
@@ -109,7 +109,7 @@
  * // block until the operation has been completed. Alternatively the required
  * // netlink message can be built using rtnl_neigh_build_delete_request()
  * // to be sent out using nl_send_auto_complete().
- * rtnl_neigh_delete(handle, neigh, 0);
+ * rtnl_neigh_delete(sk, neigh, 0);
  *
  * // Free the memory
  * rtnl_neigh_put(neigh);
@@ -139,7 +139,7 @@
  * // block until the operation has been completed. Alternatively the required
  * // netlink message can be built using rtnl_neigh_build_change_request()
  * // to be sent out using nl_send_auto_complete().
- * rtnl_neigh_change(handle, neigh, 0);
+ * rtnl_neigh_add(sk, neigh, NLM_F_REPLACE);
  *
  * // Free the memory
  * rtnl_neigh_put(neigh);
@@ -187,15 +187,13 @@
 
 	if (src->n_lladdr)
 		if (!(dst->n_lladdr = nl_addr_clone(src->n_lladdr)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	if (src->n_dst)
 		if (!(dst->n_dst = nl_addr_clone(src->n_dst)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
 static int neigh_compare(struct nl_object *_a, struct nl_object *_b,
@@ -213,7 +211,7 @@
 	diff |= NEIGH_DIFF(LLADDR,	nl_addr_cmp(a->n_lladdr, b->n_lladdr));
 	diff |= NEIGH_DIFF(DST,		nl_addr_cmp(a->n_dst, b->n_dst));
 
-	if (flags & LOOSE_FLAG_COMPARISON) {
+	if (flags & LOOSE_COMPARISON) {
 		diff |= NEIGH_DIFF(STATE,
 				  (a->n_state ^ b->n_state) & b->n_state_mask);
 		diff |= NEIGH_DIFF(FLAGS,
@@ -261,7 +259,7 @@
 
 	neigh = rtnl_neigh_alloc();
 	if (!neigh) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 
@@ -283,18 +281,22 @@
 			   NEIGH_ATTR_TYPE);
 
 	if (tb[NDA_LLADDR]) {
-		neigh->n_lladdr = nla_get_addr(tb[NDA_LLADDR], AF_UNSPEC);
-		if (!neigh->n_lladdr)
+		neigh->n_lladdr = nl_addr_alloc_attr(tb[NDA_LLADDR], AF_UNSPEC);
+		if (!neigh->n_lladdr) {
+			err = -NLE_NOMEM;
 			goto errout;
+		}
 		nl_addr_set_family(neigh->n_lladdr,
 				   nl_addr_guess_family(neigh->n_lladdr));
 		neigh->ce_mask |= NEIGH_ATTR_LLADDR;
 	}
 
 	if (tb[NDA_DST]) {
-		neigh->n_dst = nla_get_addr(tb[NDA_DST], neigh->n_family);
-		if (!neigh->n_dst)
+		neigh->n_dst = nl_addr_alloc_attr(tb[NDA_DST], neigh->n_family);
+		if (!neigh->n_dst) {
+			err = -NLE_NOMEM;
 			goto errout;
+		}
 		neigh->ce_mask |= NEIGH_ATTR_DST;
 	}
 
@@ -315,23 +317,18 @@
 	}
 
 	err = pp->pp_cb((struct nl_object *) neigh, pp);
-	if (err < 0)
-		goto errout;
-
-	err = P_ACCEPT;
-
 errout:
 	rtnl_neigh_put(neigh);
 	return err;
 }
 
-static int neigh_request_update(struct nl_cache *c, struct nl_handle *h)
+static int neigh_request_update(struct nl_cache *c, struct nl_sock *h)
 {
 	return nl_rtgen_request(h, RTM_GETNEIGH, AF_UNSPEC, NLM_F_DUMP);
 }
 
 
-static int neigh_dump_brief(struct nl_object *a, struct nl_dump_params *p)
+static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p)
 {
 	char dst[INET6_ADDRSTRLEN+5], lladdr[INET6_ADDRSTRLEN+5];
 	struct rtnl_neigh *n = (struct rtnl_neigh *) a;
@@ -340,162 +337,95 @@
 
 	link_cache = nl_cache_mngt_require("route/link");
 
-	dp_dump(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst)));
+	nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst)));
 
 	if (link_cache)
-		dp_dump(p, "dev %s ",
+		nl_dump(p, "dev %s ",
 			rtnl_link_i2name(link_cache, n->n_ifindex,
 					 state, sizeof(state)));
 	else
-		dp_dump(p, "dev %d ", n->n_ifindex);
+		nl_dump(p, "dev %d ", n->n_ifindex);
 
 	if (n->ce_mask & NEIGH_ATTR_LLADDR)
-		dp_dump(p, "lladdr %s ",
+		nl_dump(p, "lladdr %s ",
 			nl_addr2str(n->n_lladdr, lladdr, sizeof(lladdr)));
 
 	rtnl_neigh_state2str(n->n_state, state, sizeof(state));
 	rtnl_neigh_flags2str(n->n_flags, flags, sizeof(flags));
 
 	if (state[0])
-		dp_dump(p, "<%s", state);
+		nl_dump(p, "<%s", state);
 	if (flags[0])
-		dp_dump(p, "%s%s", state[0] ? "," : "<", flags);
+		nl_dump(p, "%s%s", state[0] ? "," : "<", flags);
 	if (state[0] || flags[0])
-		dp_dump(p, ">");
-	dp_dump(p, "\n");
-
-	return 1;
+		nl_dump(p, ">");
+	nl_dump(p, "\n");
 }
 
-static int neigh_dump_full(struct nl_object *a, struct nl_dump_params *p)
+static void neigh_dump_details(struct nl_object *a, struct nl_dump_params *p)
 {
 	char rtn_type[32];
 	struct rtnl_neigh *n = (struct rtnl_neigh *) a;
 	int hz = nl_get_hz();
 
-	int line = neigh_dump_brief(a, p);
+	neigh_dump_line(a, p);
 
-	dp_dump_line(p, line++, "    refcnt %u type %s confirmed %u used "
+	nl_dump_line(p, "    refcnt %u type %s confirmed %u used "
 				"%u updated %u\n",
 		n->n_cacheinfo.nci_refcnt,
 		nl_rtntype2str(n->n_type, rtn_type, sizeof(rtn_type)),
 		n->n_cacheinfo.nci_confirmed/hz,
 		n->n_cacheinfo.nci_used/hz, n->n_cacheinfo.nci_updated/hz);
-
-	return line;
 }
 
-static int neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p)
+static void neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p)
 {
-	return neigh_dump_full(a, p);
+	neigh_dump_details(a, p);
 }
 
-static int neigh_dump_xml(struct nl_object *obj, struct nl_dump_params *p)
+static void neigh_dump_env(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj;
 	char buf[128];
-	int line = 0;
 
-	dp_dump_line(p, line++, "<neighbour>\n");
-	dp_dump_line(p, line++, "  <family>%s</family>\n",
+	nl_dump_line(p, "NEIGH_FAMILY=%s\n",
 		     nl_af2str(neigh->n_family, buf, sizeof(buf)));
 
 	if (neigh->ce_mask & NEIGH_ATTR_LLADDR)
-		dp_dump_line(p, line++, "  <lladdr>%s</lladdr>\n",
+		nl_dump_line(p, "NEIGHT_LLADDR=%s\n",
 			     nl_addr2str(neigh->n_lladdr, buf, sizeof(buf)));
 
 	if (neigh->ce_mask & NEIGH_ATTR_DST)
-		dp_dump_line(p, line++, "  <dst>%s</dst>\n",
-			     nl_addr2str(neigh->n_dst, buf, sizeof(buf)));
-
-	if (neigh->ce_mask & NEIGH_ATTR_IFINDEX) {
-		struct nl_cache *link_cache;
-	
-		link_cache = nl_cache_mngt_require("route/link");
-
-		if (link_cache)
-			dp_dump_line(p, line++, "  <device>%s</device>\n",
-				     rtnl_link_i2name(link_cache,
-						      neigh->n_ifindex,
-						      buf, sizeof(buf)));
-		else
-			dp_dump_line(p, line++, "  <device>%u</device>\n",
-				     neigh->n_ifindex);
-	}
-
-	if (neigh->ce_mask & NEIGH_ATTR_PROBES)
-		dp_dump_line(p, line++, "  <probes>%u</probes>\n",
-			     neigh->n_probes);
-
-	if (neigh->ce_mask & NEIGH_ATTR_TYPE)
-		dp_dump_line(p, line++, "  <type>%s</type>\n",
-			     nl_rtntype2str(neigh->n_type, buf, sizeof(buf)));
-
-	rtnl_neigh_flags2str(neigh->n_flags, buf, sizeof(buf));
-	if (buf[0])
-		dp_dump_line(p, line++, "  <flags>%s</flags>\n", buf);
-
-	rtnl_neigh_state2str(neigh->n_state, buf, sizeof(buf));
-	if (buf[0])
-		dp_dump_line(p, line++, "  <state>%s</state>\n", buf);
-
-	dp_dump_line(p, line++, "</neighbour>\n");
-
-#if 0
-	struct rtnl_ncacheinfo n_cacheinfo;
-#endif
-
-	return line;
-}
-
-static int neigh_dump_env(struct nl_object *obj, struct nl_dump_params *p)
-{
-	struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj;
-	char buf[128];
-	int line = 0;
-
-	dp_dump_line(p, line++, "NEIGH_FAMILY=%s\n",
-		     nl_af2str(neigh->n_family, buf, sizeof(buf)));
-
-	if (neigh->ce_mask & NEIGH_ATTR_LLADDR)
-		dp_dump_line(p, line++, "NEIGHT_LLADDR=%s\n",
-			     nl_addr2str(neigh->n_lladdr, buf, sizeof(buf)));
-
-	if (neigh->ce_mask & NEIGH_ATTR_DST)
-		dp_dump_line(p, line++, "NEIGH_DST=%s\n",
+		nl_dump_line(p, "NEIGH_DST=%s\n",
 			     nl_addr2str(neigh->n_dst, buf, sizeof(buf)));
 
 	if (neigh->ce_mask & NEIGH_ATTR_IFINDEX) {
 		struct nl_cache *link_cache;
 
-		dp_dump_line(p, line++, "NEIGH_IFINDEX=%u\n",
-			     neigh->n_ifindex);
+		nl_dump_line(p, "NEIGH_IFINDEX=%u\n", neigh->n_ifindex);
 
 		link_cache = nl_cache_mngt_require("route/link");
 		if (link_cache)
-			dp_dump_line(p, line++, "NEIGH_IFNAME=%s\n",
+			nl_dump_line(p, "NEIGH_IFNAME=%s\n",
 				     rtnl_link_i2name(link_cache,
 						      neigh->n_ifindex,
 						      buf, sizeof(buf)));
 	}
 
 	if (neigh->ce_mask & NEIGH_ATTR_PROBES)
-		dp_dump_line(p, line++, "NEIGH_PROBES=%u\n",
-			     neigh->n_probes);
+		nl_dump_line(p, "NEIGH_PROBES=%u\n", neigh->n_probes);
 
 	if (neigh->ce_mask & NEIGH_ATTR_TYPE)
-		dp_dump_line(p, line++, "NEIGH_TYPE=%s\n",
+		nl_dump_line(p, "NEIGH_TYPE=%s\n",
 			     nl_rtntype2str(neigh->n_type, buf, sizeof(buf)));
 
 	rtnl_neigh_flags2str(neigh->n_flags, buf, sizeof(buf));
 	if (buf[0])
-		dp_dump_line(p, line++, "NEIGH_FLAGS=%s\n", buf);
+		nl_dump_line(p, "NEIGH_FLAGS=%s\n", buf);
 
 	rtnl_neigh_state2str(neigh->n_state, buf, sizeof(buf));
 	if (buf[0])
-		dp_dump_line(p, line++, "NEIGH_STATE=%s\n", buf);
-
-	return line;
+		nl_dump_line(p, "NEIGH_STATE=%s\n", buf);
 }
 
 /**
@@ -522,31 +452,17 @@
 
 /**
  * Build a neighbour cache including all neighbours currently configured in the kernel.
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
+ * @arg result		Pointer to store resulting cache.
  *
  * Allocates a new neighbour cache, initializes it properly and updates it
  * to include all neighbours currently configured in the kernel.
  *
- * @note The caller is responsible for destroying and freeing the
- *       cache after using it.
- * @return The new cache or NULL if an error occured.
+ * @return 0 on success or a negative error code.
  */
-struct nl_cache *rtnl_neigh_alloc_cache(struct nl_handle *handle)
+int rtnl_neigh_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
 {
-	struct nl_cache *cache;
-
-	cache = nl_cache_alloc(&rtnl_neigh_ops);
-	if (cache == NULL)
-		return NULL;
-
-	if (handle && nl_cache_refill(handle, cache) < 0) {
-		nl_cache_free(cache);
-		return NULL;
-	}
-
-	NL_DBG(2, "Returning new cache %p\n", cache);
-
-	return cache;
+	return nl_cache_alloc_and_fill(&rtnl_neigh_ops, sock, result);
 }
 
 /**
@@ -579,22 +495,26 @@
  * @{
  */
 
-static struct nl_msg * build_neigh_msg(struct rtnl_neigh *tmpl, int cmd,
-				       int flags)
+static int build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags,
+			   struct nl_msg **result)
 {
 	struct nl_msg *msg;
 	struct ndmsg nhdr = {
 		.ndm_ifindex = tmpl->n_ifindex,
-		.ndm_family = nl_addr_get_family(tmpl->n_dst),
 		.ndm_state = NUD_PERMANENT,
 	};
 
+	if (!(tmpl->ce_mask & NEIGH_ATTR_DST))
+		return -NLE_MISSING_ATTR;
+
+	nhdr.ndm_family = nl_addr_get_family(tmpl->n_dst);
+
 	if (tmpl->ce_mask & NEIGH_ATTR_STATE)
 		nhdr.ndm_state = tmpl->n_state;
 
 	msg = nlmsg_alloc_simple(cmd, flags);
 	if (!msg)
-		return NULL;
+		return -NLE_NOMEM;
 
 	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
 		goto nla_put_failure;
@@ -604,17 +524,19 @@
 	if (tmpl->ce_mask & NEIGH_ATTR_LLADDR)
 		NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr);
 
-	return msg;
+	*result = msg;
+	return 0;
 
 nla_put_failure:
 	nlmsg_free(msg);
-	return NULL;
+	return -NLE_MSGSIZE;
 }
 
 /**
  * Build netlink request message to add a new neighbour
  * @arg tmpl		template with data of new neighbour
  * @arg flags		additional netlink message flags
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting a addition of a new
  * neighbour. The netlink message header isn't fully equipped with
@@ -628,16 +550,17 @@
  *  - Destination address (rtnl_neigh_set_dst())
  *  - Link layer address (rtnl_neigh_set_lladdr())
  *
- * @return The netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg * rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags)
+int rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags,
+				 struct nl_msg **result)
 {
-	return build_neigh_msg(tmpl, RTM_NEWNEIGH, NLM_F_CREATE | flags);
+	return build_neigh_msg(tmpl, RTM_NEWNEIGH, flags, result);
 }
 
 /**
  * Add a new neighbour
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg tmpl		template with requested changes
  * @arg flags		additional netlink message flags
  *
@@ -653,21 +576,20 @@
  *
  * @return 0 on sucess or a negative error if an error occured.
  */
-int rtnl_neigh_add(struct nl_handle *handle, struct rtnl_neigh *tmpl, int flags)
+int rtnl_neigh_add(struct nl_sock *sk, struct rtnl_neigh *tmpl, int flags)
 {
 	int err;
 	struct nl_msg *msg;
 	
-	msg = rtnl_neigh_build_add_request(tmpl, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_neigh_build_add_request(tmpl, flags, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -681,6 +603,7 @@
  * Build a netlink request message to delete a neighbour
  * @arg neigh		neighbour to delete
  * @arg flags		additional netlink message flags
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting a deletion of a neighbour.
  * The netlink message header isn't fully equipped with all relevant
@@ -688,17 +611,17 @@
  * or supplemented as needed. \a neigh must point to an existing
  * neighbour.
  *
- * @return The netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg *rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh,
-					       int flags)
+int rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, int flags,
+				    struct nl_msg **result)
 {
-	return build_neigh_msg(neigh, RTM_DELNEIGH, flags);
+	return build_neigh_msg(neigh, RTM_DELNEIGH, flags, result);
 }
 
 /**
  * Delete a neighbour
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg neigh		neighbour to delete
  * @arg flags		additional netlink message flags
  *
@@ -708,81 +631,21 @@
  *
  * @return 0 on sucess or a negative error if an error occured.
  */
-int rtnl_neigh_delete(struct nl_handle *handle, struct rtnl_neigh *neigh,
+int rtnl_neigh_delete(struct nl_sock *sk, struct rtnl_neigh *neigh,
 		      int flags)
 {
-	int err;
 	struct nl_msg *msg;
+	int err;
 	
-	msg = rtnl_neigh_build_delete_request(neigh, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_neigh_build_delete_request(neigh, flags, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
-}
-
-/** @} */
-
-/**
- * @name Neighbour Modification
- * @{
- */
-
-/**
- * Build a netlink request message to change neighbour attributes
- * @arg neigh		the neighbour to change
- * @arg flags		additional netlink message flags
- *
- * Builds a new netlink message requesting a change of a neigh
- * attributes. The netlink message header isn't fully equipped with
- * all relevant fields and must thus be sent out via nl_send_auto_complete()
- * or supplemented as needed.
- *
- * @return The netlink message
- * @note Not all attributes can be changed, see
- *       \ref neigh_changeable "Changeable Attributes" for a list.
- */
-struct nl_msg *rtnl_neigh_build_change_request(struct rtnl_neigh *neigh,
-					       int flags)
-{
-	return build_neigh_msg(neigh, RTM_NEWNEIGH, NLM_F_REPLACE | flags);
-}
-
-/**
- * Change neighbour attributes
- * @arg handle		netlink handle
- * @arg neigh		neighbour to be changed
- * @arg flags		additional netlink message flags
- *
- * Builds a netlink message by calling rtnl_neigh_build_change_request(),
- * sends the request to the kernel and waits for the next ACK to be
- * received and thus blocks until the request has been fullfilled.
- *
- * @return 0 on sucess or a negative error if an error occured.
- * @note Not all attributes can be changed, see
- *       \ref neigh_changeable "Changeable Attributes" for a list.
- */
-int rtnl_neigh_change(struct nl_handle *handle, struct rtnl_neigh *neigh,
-		      int flags)
-{
-	int err;
-	struct nl_msg *msg;
-	
-	msg = rtnl_neigh_build_change_request(neigh, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
-
-	err = nl_send_auto_complete(handle, msg);
-	if (err < 0)
-		return err;
-
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -893,10 +756,7 @@
 
 int rtnl_neigh_get_ifindex(struct rtnl_neigh *neigh)
 {
-	if (neigh->ce_mask & NEIGH_ATTR_IFINDEX)
-		return neigh->n_ifindex;
-	else
-		return RTNL_LINK_NOT_FOUND;
+	return neigh->n_ifindex;
 }
 
 static inline int __assign_addr(struct rtnl_neigh *neigh, struct nl_addr **pos,
@@ -905,8 +765,7 @@
 	if (!nocheck) {
 		if (neigh->ce_mask & NEIGH_ATTR_FAMILY) {
 			if (new->a_family != neigh->n_family)
-				return nl_error(EINVAL,
-						"Address family mismatch");
+				return -NLE_AF_MISMATCH;
 		} else {
 			neigh->n_family = new->a_family;
 			neigh->ce_mask |= NEIGH_ATTR_FAMILY;
@@ -957,6 +816,11 @@
 	neigh->ce_mask |= NEIGH_ATTR_FAMILY;
 }
 
+int rtnl_neigh_get_family(struct rtnl_neigh *neigh)
+{
+	return neigh->n_family;
+}
+
 void rtnl_neigh_set_type(struct rtnl_neigh *neigh, int type)
 {
 	neigh->n_type = type;
@@ -978,14 +842,15 @@
 	.oo_size		= sizeof(struct rtnl_neigh),
 	.oo_free_data		= neigh_free_data,
 	.oo_clone		= neigh_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= neigh_dump_brief,
-	.oo_dump[NL_DUMP_FULL]	= neigh_dump_full,
-	.oo_dump[NL_DUMP_STATS]	= neigh_dump_stats,
-	.oo_dump[NL_DUMP_XML]	= neigh_dump_xml,
-	.oo_dump[NL_DUMP_ENV]	= neigh_dump_env,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= neigh_dump_line,
+	    [NL_DUMP_DETAILS]	= neigh_dump_details,
+	    [NL_DUMP_STATS]	= neigh_dump_stats,
+	    [NL_DUMP_ENV]	= neigh_dump_env,
+	},
 	.oo_compare		= neigh_compare,
 	.oo_attrs2str		= neigh_attrs2str,
-	.oo_id_attrs		= (NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY),
+	.oo_id_attrs		= (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY),
 };
 
 static struct nl_af_group neigh_groups[] = {
diff --git a/lib/route/neightbl.c b/lib/route/neightbl.c
index 3191b5b..9599faa 100644
--- a/lib/route/neightbl.c
+++ b/lib/route/neightbl.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -129,7 +129,7 @@
 
 	ntbl = rtnl_neightbl_alloc();
 	if (!ntbl) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 
@@ -143,7 +143,7 @@
 	ntbl->nt_family = rtmsg->rtgen_family;
 
 	if (tb[NDTA_NAME] == NULL) {
-		err = nl_error(EINVAL, "NDTA_NAME is missing");
+		return -NLE_MISSING_ATTR;
 		goto errout;
 	}
 
@@ -217,27 +217,22 @@
 	}
 
 	err = pp->pp_cb((struct nl_object *) ntbl, pp);
-	if (err < 0)
-		goto errout;
-
-	err = P_ACCEPT;
 errout:
 	rtnl_neightbl_put(ntbl);
 	return err;
 }
 
-static int neightbl_request_update(struct nl_cache *c, struct nl_handle *h)
+static int neightbl_request_update(struct nl_cache *c, struct nl_sock *h)
 {
 	return nl_rtgen_request(h, RTM_GETNEIGHTBL, AF_UNSPEC, NLM_F_DUMP);
 }
 
 
-static int neightbl_dump_brief(struct nl_object *arg, struct nl_dump_params *p)
+static void neightbl_dump_line(struct nl_object *arg, struct nl_dump_params *p)
 {
-	int line = 1;
 	struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg;
 
-	dp_dump(p, "%s", ntbl->nt_name);
+	nl_dump_line(p, "%s", ntbl->nt_name);
 
 	if (ntbl->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) {
 		struct nl_cache *link_cache;
@@ -246,57 +241,52 @@
 
 		if (link_cache) {
 			char buf[32];
-			dp_dump(p, "<%s> ",
+			nl_dump(p, "<%s> ",
 				rtnl_link_i2name(link_cache,
 						 ntbl->nt_parms.ntp_ifindex,
 						 buf, sizeof(buf)));
 		} else
-			dp_dump(p, "<%u> ", ntbl->nt_parms.ntp_ifindex);
+			nl_dump(p, "<%u> ", ntbl->nt_parms.ntp_ifindex);
 	} else
-		dp_dump(p, " ");
+		nl_dump(p, " ");
 
 	if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG)
-		dp_dump(p, "entries %u ", ntbl->nt_config.ndtc_entries);
+		nl_dump(p, "entries %u ", ntbl->nt_config.ndtc_entries);
 
 	if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) {
 		char rt[32], rt2[32];
 		struct rtnl_neightbl_parms *pa = &ntbl->nt_parms;
 
-		dp_dump(p, "reachable-time %s retransmit-time %s",
+		nl_dump(p, "reachable-time %s retransmit-time %s",
 			nl_msec2str(pa->ntp_reachable_time, rt, sizeof(rt)),
 			nl_msec2str(pa->ntp_retrans_time, rt2, sizeof(rt2)));
 	}
 
-	dp_dump(p, "\n");
-
-	return line;
+	nl_dump(p, "\n");
 }
 
-static int neightbl_dump_full(struct nl_object *arg, struct nl_dump_params *p)
+static void neightbl_dump_details(struct nl_object *arg, struct nl_dump_params *p)
 {
 	char x[32], y[32], z[32];
 	struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg;
 
-	int line = neightbl_dump_brief(arg, p);
+	neightbl_dump_line(arg, p);
 
 	if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG) {
-		dp_new_line(p, line++);
-		dp_dump(p, "    key-len %u entry-size %u last-flush %s\n",
+		nl_dump_line(p, "    key-len %u entry-size %u last-flush %s\n",
 			ntbl->nt_config.ndtc_key_len,
 			ntbl->nt_config.ndtc_entry_size,
 			nl_msec2str(ntbl->nt_config.ndtc_last_flush,
 				      x, sizeof(x)));
 
-		dp_new_line(p, line++);
-		dp_dump(p, "    gc threshold %u/%u/%u interval %s " \
+		nl_dump_line(p, "    gc threshold %u/%u/%u interval %s " \
 			    "chain-position %u\n",
 			ntbl->nt_gc_thresh1, ntbl->nt_gc_thresh2,
 			ntbl->nt_gc_thresh3,
 			nl_msec2str(ntbl->nt_gc_interval, x, sizeof(x)),
 			ntbl->nt_config.ndtc_hash_chain_gc);
 
-		dp_new_line(p, line++);
-		dp_dump(p, "    hash-rand 0x%08X/0x%08X last-rand %s\n",
+		nl_dump_line(p, "    hash-rand 0x%08X/0x%08X last-rand %s\n",
 			ntbl->nt_config.ndtc_hash_rnd,
 			ntbl->nt_config.ndtc_hash_mask,
 			nl_msec2str(ntbl->nt_config.ndtc_last_rand,
@@ -306,49 +296,43 @@
 	if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) {
 		struct rtnl_neightbl_parms *pa = &ntbl->nt_parms;
 
-		dp_new_line(p, line++);
-		dp_dump(p, "    refcnt %u pending-queue-limit %u " \
+		nl_dump_line(p, "    refcnt %u pending-queue-limit %u " \
 			    "proxy-delayed-queue-limit %u\n",
 			pa->ntp_refcnt,
 			pa->ntp_queue_len,
 			pa->ntp_proxy_qlen);
 
-		dp_new_line(p, line++);
-		dp_dump(p, "    num-userspace-probes %u num-unicast-probes " \
+		nl_dump_line(p, "    num-userspace-probes %u num-unicast-probes " \
 			    "%u num-multicast-probes %u\n",
 			pa->ntp_app_probes,
 			pa->ntp_ucast_probes,
 			pa->ntp_mcast_probes);
 
-		dp_new_line(p, line++);
-		dp_dump(p, "    min-age %s base-reachable-time %s " \
+		nl_dump_line(p, "    min-age %s base-reachable-time %s " \
 			    "stale-check-interval %s\n",
 			nl_msec2str(pa->ntp_locktime, x, sizeof(x)),
 			nl_msec2str(pa->ntp_base_reachable_time,
 				      y, sizeof(y)),
 			nl_msec2str(pa->ntp_gc_stale_time, z, sizeof(z)));
 
-		dp_new_line(p, line++);
-		dp_dump(p, "    initial-probe-delay %s answer-delay %s " \
+		nl_dump_line(p, "    initial-probe-delay %s answer-delay %s " \
 			    "proxy-answer-delay %s\n",
 			nl_msec2str(pa->ntp_probe_delay, x, sizeof(x)),
 			nl_msec2str(pa->ntp_anycast_delay, y, sizeof(y)),
 			nl_msec2str(pa->ntp_proxy_delay, z, sizeof(z)));
 	}
-
-	return line;
 }
 
-static int neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p)
+static void neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p)
 {
 	struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg;
-	int line = neightbl_dump_full(arg, p);
+
+	neightbl_dump_details(arg, p);
 
 	if (!(ntbl->ce_mask & NEIGHTBL_ATTR_STATS))
-		return line;
+		return;
 
-	dp_new_line(p, line++);
-	dp_dump(p, "    lookups %lld hits %lld failed %lld " \
+	nl_dump_line(p, "    lookups %lld hits %lld failed %lld " \
 		    "allocations %lld destroys %lld\n",
 		ntbl->nt_stats.ndts_lookups,
 		ntbl->nt_stats.ndts_hits,
@@ -356,18 +340,15 @@
 		ntbl->nt_stats.ndts_allocs,
 		ntbl->nt_stats.ndts_destroys);
 
-	dp_new_line(p, line++);
-	dp_dump(p, "    hash-grows %lld forced-gc-runs %lld " \
+	nl_dump_line(p, "    hash-grows %lld forced-gc-runs %lld " \
 		    "periodic-gc-runs %lld\n",
 		ntbl->nt_stats.ndts_hash_grows,
 		ntbl->nt_stats.ndts_forced_gc_runs,
 		ntbl->nt_stats.ndts_periodic_gc_runs);
 
-	dp_dump(p, "    rcv-unicast-probes %lld rcv-multicast-probes %lld\n",
+	nl_dump_line(p, "    rcv-unicast-probes %lld rcv-multicast-probes %lld\n",
 		ntbl->nt_stats.ndts_rcv_probes_ucast,
 		ntbl->nt_stats.ndts_rcv_probes_mcast);
-
-	return line;
 }
 
 /**
@@ -394,30 +375,18 @@
 
 /**
  * Build a neighbour table cache including all neighbour tables currently configured in the kernel.
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
+ * @arg result		Pointer to store resulting cache.
  *
  * Allocates a new neighbour table cache, initializes it properly and
  * updates it to include all neighbour tables currently configured in
  * the kernel.
  *
- * @note The caller is responsible for destroying and freeing the
- *       cache after using it.
- * @return The new cache or NULL if an error occured.
+ * @return 0 on success or a negative error code.
  */
-struct nl_cache * rtnl_neightbl_alloc_cache(struct nl_handle *handle)
+int rtnl_neightbl_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
 {
-	struct nl_cache * cache;
-	
-	cache = nl_cache_alloc(&rtnl_neightbl_ops);
-	if (cache == NULL)
-		return NULL;
-	
-	if (handle && nl_cache_refill(handle, cache) < 0) {
-		nl_cache_free(cache);
-		return NULL;
-	}
-
-	return cache;
+	return nl_cache_alloc_and_fill(&rtnl_neightbl_ops, sk, result);
 }
 
 /**
@@ -464,6 +433,7 @@
  * Builds a netlink change request message to change neighbour table attributes
  * @arg old		neighbour table to change
  * @arg tmpl		template with requested changes
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting a change of neighbour table
  * attributes. The netlink message header isn't fully equipped with all
@@ -473,98 +443,115 @@
  * kernel and \a tmpl must contain the attributes to be changed set via
  * \c rtnl_neightbl_set_* functions.
  *
- * @return New netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg * rtnl_neightbl_build_change_request(struct rtnl_neightbl *old,
-						   struct rtnl_neightbl *tmpl)
+int rtnl_neightbl_build_change_request(struct rtnl_neightbl *old,
+				       struct rtnl_neightbl *tmpl,
+				       struct nl_msg **result)
 {
-	struct nl_msg *m;
+	struct nl_msg *m, *parms = NULL;
 	struct ndtmsg ndt = {
 		.ndtm_family = old->nt_family,
 	};
 
 	m = nlmsg_alloc_simple(RTM_SETNEIGHTBL, 0);
-	nlmsg_append(m, &ndt, sizeof(ndt), NLMSG_ALIGNTO);
+	if (!m)
+		return -NLE_NOMEM;
 
-	nla_put_string(m, NDTA_NAME, old->nt_name);
+	if (nlmsg_append(m, &ndt, sizeof(ndt), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	NLA_PUT_STRING(m, NDTA_NAME, old->nt_name);
 
 	if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH1)
-		nla_put_u32(m, NDTA_THRESH1, tmpl->nt_gc_thresh1);
+		NLA_PUT_U32(m, NDTA_THRESH1, tmpl->nt_gc_thresh1);
 
 	if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2)
-		nla_put_u32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2);
+		NLA_PUT_U32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2);
 
 	if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2)
-		nla_put_u32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2);
+		NLA_PUT_U32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2);
 
 	if (tmpl->ce_mask & NEIGHTBL_ATTR_GC_INTERVAL)
-		nla_put_u64(m, NDTA_GC_INTERVAL,
+		NLA_PUT_U64(m, NDTA_GC_INTERVAL,
 				      tmpl->nt_gc_interval);
 
 	if (tmpl->ce_mask & NEIGHTBL_ATTR_PARMS) {
 		struct rtnl_neightbl_parms *p = &tmpl->nt_parms;
-		struct nl_msg *parms = nlmsg_alloc();
+
+		parms = nlmsg_alloc();
+		if (!parms)
+			goto nla_put_failure;
 
 		if (old->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX)
-			nla_put_u32(parms, NDTPA_IFINDEX,
+			NLA_PUT_U32(parms, NDTPA_IFINDEX,
 					      old->nt_parms.ntp_ifindex);
 
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_QUEUE_LEN)
-			nla_put_u32(parms, NDTPA_QUEUE_LEN, p->ntp_queue_len);
+			NLA_PUT_U32(parms, NDTPA_QUEUE_LEN, p->ntp_queue_len);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_APP_PROBES)
-			nla_put_u32(parms, NDTPA_APP_PROBES, p->ntp_app_probes);
+			NLA_PUT_U32(parms, NDTPA_APP_PROBES, p->ntp_app_probes);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_UCAST_PROBES)
-			nla_put_u32(parms, NDTPA_UCAST_PROBES,
+			NLA_PUT_U32(parms, NDTPA_UCAST_PROBES,
 				    p->ntp_ucast_probes);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_MCAST_PROBES)
-			nla_put_u32(parms, NDTPA_MCAST_PROBES,
+			NLA_PUT_U32(parms, NDTPA_MCAST_PROBES,
 				    p->ntp_mcast_probes);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_QLEN)
-			nla_put_u32(parms, NDTPA_PROXY_QLEN,
+			NLA_PUT_U32(parms, NDTPA_PROXY_QLEN,
 				    p->ntp_proxy_qlen);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME)
-			nla_put_u64(parms, NDTPA_BASE_REACHABLE_TIME,
+			NLA_PUT_U64(parms, NDTPA_BASE_REACHABLE_TIME,
 				    p->ntp_base_reachable_time);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_RETRANS_TIME)
-			nla_put_u64(parms, NDTPA_RETRANS_TIME,
+			NLA_PUT_U64(parms, NDTPA_RETRANS_TIME,
 				    p->ntp_retrans_time);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_GC_STALETIME)
-			nla_put_u64(parms, NDTPA_GC_STALETIME,
+			NLA_PUT_U64(parms, NDTPA_GC_STALETIME,
 				    p->ntp_gc_stale_time);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME)
-			nla_put_u64(parms, NDTPA_DELAY_PROBE_TIME,
+			NLA_PUT_U64(parms, NDTPA_DELAY_PROBE_TIME,
 				    p->ntp_proxy_delay);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_ANYCAST_DELAY)
-			nla_put_u64(parms, NDTPA_ANYCAST_DELAY,
+			NLA_PUT_U64(parms, NDTPA_ANYCAST_DELAY,
 				    p->ntp_anycast_delay);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_DELAY)
-			nla_put_u64(parms, NDTPA_PROXY_DELAY,
+			NLA_PUT_U64(parms, NDTPA_PROXY_DELAY,
 					      p->ntp_proxy_delay);
 
 		if (p->ntp_mask & NEIGHTBLPARM_ATTR_LOCKTIME)
-			nla_put_u64(parms, NDTPA_LOCKTIME, p->ntp_locktime);
+			NLA_PUT_U64(parms, NDTPA_LOCKTIME, p->ntp_locktime);
 
-		nla_put_nested(m, NDTA_PARMS, parms);
+		if (nla_put_nested(m, NDTA_PARMS, parms) < 0)
+			goto nla_put_failure;
+
 		nlmsg_free(parms);
 	}
-	
-	return m;
+
+	*result = m;
+	return 0;
+
+nla_put_failure:
+	if (parms)
+		nlmsg_free(parms);
+	nlmsg_free(m);
+	return -NLE_MSGSIZE;
 }
 
 /**
  * Change neighbour table attributes
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg old		neighbour table to be changed
  * @arg tmpl		template with requested changes
  *
@@ -575,19 +562,21 @@
  *
  * @return 0 on success or a negative error code
  */
-int rtnl_neightbl_change(struct nl_handle *handle, struct rtnl_neightbl *old,
+int rtnl_neightbl_change(struct nl_sock *sk, struct rtnl_neightbl *old,
 			 struct rtnl_neightbl *tmpl)
 {
-	int err;
 	struct nl_msg *msg;
+	int err;
 	
-	msg = rtnl_neightbl_build_change_request(old, tmpl);
-	err = nl_send_auto_complete(handle, msg);
+	if ((err = rtnl_neightbl_build_change_request(old, tmpl, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -790,9 +779,11 @@
 static struct nl_object_ops neightbl_obj_ops = {
 	.oo_name		= "route/neightbl",
 	.oo_size		= sizeof(struct rtnl_neightbl),
-	.oo_dump[NL_DUMP_BRIEF]	= neightbl_dump_brief,
-	.oo_dump[NL_DUMP_FULL]	= neightbl_dump_full,
-	.oo_dump[NL_DUMP_STATS]	= neightbl_dump_stats,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= neightbl_dump_line,
+	    [NL_DUMP_DETAILS]	= neightbl_dump_details,
+	    [NL_DUMP_STATS]	= neightbl_dump_stats,
+	},
 	.oo_compare		= neightbl_compare,
 };
 
diff --git a/lib/route/nexthop.c b/lib/route/nexthop.c
index 7486769..788e255 100644
--- a/lib/route/nexthop.c
+++ b/lib/route/nexthop.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -21,6 +21,14 @@
 #include <netlink/route/rtnl.h>
 #include <netlink/route/route.h>
 
+/** @cond SKIP */
+#define NH_ATTR_FLAGS   0x000001
+#define NH_ATTR_WEIGHT  0x000002
+#define NH_ATTR_IFINDEX 0x000004
+#define NH_ATTR_GATEWAY 0x000008
+#define NH_ATTR_REALMS  0x000010
+/** @endcond */
+
 /**
  * @name Allocation/Freeing
  * @{
@@ -31,10 +39,8 @@
 	struct rtnl_nexthop *nh;
 
 	nh = calloc(1, sizeof(*nh));
-	if (!nh) {
-		nl_errno(ENOMEM);
+	if (!nh)
 		return NULL;
-	}
 
 	nl_init_list_head(&nh->rtnh_list);
 
@@ -53,7 +59,7 @@
 	nh->rtnh_flag_mask = src->rtnh_flag_mask;
 	nh->rtnh_weight = src->rtnh_weight;
 	nh->rtnh_ifindex = src->rtnh_ifindex;
-	nh->rtnh_mask = src->rtnh_mask;
+	nh->ce_mask = src->ce_mask;
 
 	if (src->rtnh_gateway) {
 		nh->rtnh_gateway = nl_addr_clone(src->rtnh_gateway);
@@ -74,78 +80,251 @@
 
 /** @} */
 
-/**
- * @name Attributes
- */
-
-void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, int weight)
+int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b,
+			  uint32_t attrs, int loose)
 {
-	nh->rtnh_weight = weight;
-	nh->rtnh_mask |= NEXTHOP_HAS_WEIGHT;
+	int diff = 0;
+
+#define NH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NH_ATTR_##ATTR, a, b, EXPR)
+
+	diff |= NH_DIFF(IFINDEX,	a->rtnh_ifindex != b->rtnh_ifindex);
+	diff |= NH_DIFF(WEIGHT,		a->rtnh_weight != b->rtnh_weight);
+	diff |= NH_DIFF(REALMS,		a->rtnh_realms != b->rtnh_realms);
+	diff |= NH_DIFF(GATEWAY,	nl_addr_cmp(a->rtnh_gateway,
+						    b->rtnh_gateway));
+
+	if (loose)
+		diff |= NH_DIFF(FLAGS,
+			  (a->rtnh_flags ^ b->rtnh_flags) & b->rtnh_flag_mask);
+	else
+		diff |= NH_DIFF(FLAGS, a->rtnh_flags != b->rtnh_flags);
+	
+#undef NH_DIFF
+
+	return diff;
 }
 
-int rtnl_route_nh_get_weight(struct rtnl_nexthop *nh)
+static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
 {
-	if (nh->rtnh_mask & NEXTHOP_HAS_WEIGHT)
-		return nh->rtnh_weight;
-	else
-		return 0;
+	struct nl_cache *link_cache;
+	char buf[128];
+
+	link_cache = nl_cache_mngt_require("route/link");
+
+	nl_dump(dp, "via");
+
+	if (nh->ce_mask & NH_ATTR_GATEWAY)
+		nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway,
+						   buf, sizeof(buf)));
+
+	if(nh->ce_mask & NH_ATTR_IFINDEX) {
+		if (link_cache) {
+			nl_dump(dp, " dev %s",
+				rtnl_link_i2name(link_cache,
+						 nh->rtnh_ifindex,
+						 buf, sizeof(buf)));
+		} else
+			nl_dump(dp, " dev %d", nh->rtnh_ifindex);
+	}
+
+	nl_dump(dp, " ");
+}
+
+static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
+{
+	struct nl_cache *link_cache;
+	char buf[128];
+
+	link_cache = nl_cache_mngt_require("route/link");
+
+	nl_dump(dp, "nexthop");
+
+	if (nh->ce_mask & NH_ATTR_GATEWAY)
+		nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway,
+						   buf, sizeof(buf)));
+
+	if(nh->ce_mask & NH_ATTR_IFINDEX) {
+		if (link_cache) {
+			nl_dump(dp, " dev %s",
+				rtnl_link_i2name(link_cache,
+						 nh->rtnh_ifindex,
+						 buf, sizeof(buf)));
+		} else
+			nl_dump(dp, " dev %d", nh->rtnh_ifindex);
+	}
+
+	if (nh->ce_mask & NH_ATTR_WEIGHT)
+		nl_dump(dp, " weight %u", nh->rtnh_weight);
+
+	if (nh->ce_mask & NH_ATTR_REALMS)
+		nl_dump(dp, " realm %04x:%04x",
+			RTNL_REALM_FROM(nh->rtnh_realms),
+			RTNL_REALM_TO(nh->rtnh_realms));
+
+	if (nh->ce_mask & NH_ATTR_FLAGS)
+		nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags,
+							buf, sizeof(buf)));
+}
+
+static void nh_dump_env(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
+{
+	struct nl_cache *link_cache;
+	char buf[128];
+
+	link_cache = nl_cache_mngt_require("route/link");
+
+	if (nh->ce_mask & NH_ATTR_GATEWAY)
+		nl_dump_line(dp, "ROUTE_NH%d_VIA=%s\n", dp->dp_ivar,
+			nl_addr2str(nh->rtnh_gateway, buf, sizeof(buf)));
+
+	if(nh->ce_mask & NH_ATTR_IFINDEX) {
+		if (link_cache) {
+			nl_dump_line(dp, "ROUTE_NH%d_DEV=%s\n", dp->dp_ivar,
+					rtnl_link_i2name(link_cache,
+						 nh->rtnh_ifindex,
+						 buf, sizeof(buf)));
+		} else
+			nl_dump_line(dp, "ROUTE_NH%d_DEV=%d\n", dp->dp_ivar,
+					nh->rtnh_ifindex);
+	}
+
+	if (nh->ce_mask & NH_ATTR_WEIGHT)
+		nl_dump_line(dp, "ROUTE_NH%d_WEIGHT=%u\n", dp->dp_ivar,
+				nh->rtnh_weight);
+
+	if (nh->ce_mask & NH_ATTR_REALMS)
+		nl_dump_line(dp, "ROUTE_NH%d_REALM=%04x:%04x\n", dp->dp_ivar,
+			RTNL_REALM_FROM(nh->rtnh_realms),
+			RTNL_REALM_TO(nh->rtnh_realms));
+
+	if (nh->ce_mask & NH_ATTR_FLAGS)
+		nl_dump_line(dp, "ROUTE_NH%d_FLAGS=<%s>\n", dp->dp_ivar,
+			rtnl_route_nh_flags2str(nh->rtnh_flags,
+							buf, sizeof(buf)));
+}
+void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
+{
+	switch (dp->dp_type) {
+	case NL_DUMP_LINE:
+		nh_dump_line(nh, dp);
+		break;
+
+	case NL_DUMP_DETAILS:
+	case NL_DUMP_STATS:
+		if (dp->dp_ivar == NH_DUMP_FROM_DETAILS)
+			nh_dump_details(nh, dp);
+		break;
+
+	case NL_DUMP_ENV:
+		nh_dump_env(nh, dp);
+		break;
+	
+	default:
+		break;
+	}
+}
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, uint8_t weight)
+{
+	nh->rtnh_weight = weight;
+	nh->ce_mask |= NH_ATTR_WEIGHT;
+}
+
+uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *nh)
+{
+	return nh->rtnh_weight;
 }
 
 void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *nh, int ifindex)
 {
 	nh->rtnh_ifindex = ifindex;
-	nh->rtnh_mask |= NEXTHOP_HAS_IFINDEX;
+	nh->ce_mask |= NH_ATTR_IFINDEX;
 }
 
 int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh)
 {
-	if (nh->rtnh_mask & NEXTHOP_HAS_IFINDEX)
-		return nh->rtnh_ifindex;
-	else
-		return -1;
+	return nh->rtnh_ifindex;
 }	
 
 void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr)
 {
 	struct nl_addr *old = nh->rtnh_gateway;
 
-	nh->rtnh_gateway = nl_addr_get(addr);
+	if (addr) {
+		nh->rtnh_gateway = nl_addr_get(addr);
+		nh->ce_mask |= NH_ATTR_GATEWAY;
+	} else {
+		nh->ce_mask &= ~NH_ATTR_GATEWAY;
+		nh->rtnh_gateway = NULL;
+	}
+
 	if (old)
 		nl_addr_put(old);
-
-	nh->rtnh_mask |= NEXTHOP_HAS_GATEWAY;
 }
 
 struct nl_addr *rtnl_route_nh_get_gateway(struct rtnl_nexthop *nh)
 {
-	if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY)
-		return nh->rtnh_gateway;
-	else
-		return NULL;
+	return nh->rtnh_gateway;
 }
 
 void rtnl_route_nh_set_flags(struct rtnl_nexthop *nh, unsigned int flags)
 {
 	nh->rtnh_flag_mask |= flags;
 	nh->rtnh_flags |= flags;
-	nh->rtnh_mask |= NEXTHOP_HAS_FLAGS;
+	nh->ce_mask |= NH_ATTR_FLAGS;
 }
 
 void rtnl_route_nh_unset_flags(struct rtnl_nexthop *nh, unsigned int flags)
 {
 	nh->rtnh_flag_mask |= flags;
 	nh->rtnh_flags &= ~flags;
-	nh->rtnh_mask |= NEXTHOP_HAS_FLAGS;
+	nh->ce_mask |= NH_ATTR_FLAGS;
 }
 
 unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *nh)
 {
-	if (nh->rtnh_mask & NEXTHOP_HAS_FLAGS)
-		return nh->rtnh_flags;
-	else
-		return 0;
+	return nh->rtnh_flags;
+}
+
+void rtnl_route_nh_set_realms(struct rtnl_nexthop *nh, uint32_t realms)
+{
+	nh->rtnh_realms = realms;
+	nh->ce_mask |= NH_ATTR_REALMS;
+}
+
+uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh)
+{
+	return nh->rtnh_realms;
 }
 
 /** @} */
+
+/**
+ * @name Nexthop Flags Translations
+ * @{
+ */
+
+static struct trans_tbl nh_flags[] = {
+	__ADD(RTNH_F_DEAD, dead)
+	__ADD(RTNH_F_PERVASIVE, pervasive)
+	__ADD(RTNH_F_ONLINK, onlink)
+};
+
+char *rtnl_route_nh_flags2str(int flags, char *buf, size_t len)
+{
+	return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags));
+}
+
+int rtnl_route_nh_str2flags(const char *name)
+{
+	return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags));
+}
+
+/** @} */
+
 /** @} */
diff --git a/lib/route/pktloc.c b/lib/route/pktloc.c
new file mode 100644
index 0000000..f0d0155
--- /dev/null
+++ b/lib/route/pktloc.c
@@ -0,0 +1,168 @@
+/*
+ * lib/route/pktloc.c     Packet Location Aliasing
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation version 2 of the License.
+ *
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup tc
+ * @defgroup pktloc Packet Location Aliasing
+ * Packet Location Aliasing
+ *
+ * The packet location aliasing interface eases the use of offset definitions
+ * inside packets by allowing them to be referenced by name. Known positions
+ * of protocol fields are stored in a configuration file and associated with
+ * a name for later reference. The configuration file is distributed with the
+ * library and provides a well defined set of definitions for most common
+ * protocol fields.
+ *
+ * @subsection pktloc_examples Examples
+ * @par Example 1.1 Looking up a packet location
+ * @code
+ * struct rtnl_pktloc *loc;
+ *
+ * rtnl_pktloc_lookup("ip.src", &loc);
+ * @endcode
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/pktloc.h>
+
+#include "pktloc_syntax.h"
+#include "pktloc_grammar.h"
+
+/** @cond */
+#define PKTLOC_NAME_HT_SIZ 256
+
+static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ];
+
+/* djb2 */
+unsigned int pktloc_hash(const char *str)
+{
+	unsigned long hash = 5381;
+	int c;
+
+	while ((c = *str++))
+		hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+	return hash % PKTLOC_NAME_HT_SIZ;
+}
+
+
+void rtnl_pktloc_add(struct rtnl_pktloc *loc)
+{
+	nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]);
+}
+
+extern int pktloc_parse(void *scanner);
+
+/** @endcond */
+
+static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
+{
+	if (!loc)
+		return;
+
+	free(loc->name);
+	free(loc);
+}
+
+static int read_pktlocs(void)
+{
+	YY_BUFFER_STATE buf;
+	yyscan_t scanner = NULL;
+	static time_t last_read;
+	struct stat st = {0};
+	char *path;
+	int i, err;
+	FILE *fd;
+
+	asprintf(&path, "%s/pktloc", SYSCONFDIR);
+
+	/* if stat fails, just try to read the file */
+	if (stat(path, &st) == 0) {
+		/* Don't re-read file if file is unchanged */
+		if (last_read == st.st_mtime)
+			return 0;
+	}
+
+	if (!(fd = fopen(path, "r")))
+		return -NLE_PKTLOC_FILE;
+
+	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) {
+		struct rtnl_pktloc *loc, *n;
+
+		nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list)
+			rtnl_pktloc_free(loc);
+
+		nl_init_list_head(&pktloc_name_ht[i]);
+	}
+
+	if ((err = pktloc_lex_init(&scanner)) < 0)
+		return -NLE_FAILURE;
+
+	buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner);
+	pktloc__switch_to_buffer(buf, scanner);
+
+	if ((err = pktloc_parse(scanner)) < 0)
+		return -NLE_FAILURE;
+
+	if (scanner)
+		pktloc_lex_destroy(scanner);
+
+	free(path);
+	last_read = st.st_mtime;
+
+	return 0;
+}
+
+/**
+ * Lookup packet location alias
+ * @arg name		Name of packet location.
+ *
+ * Tries to find a matching packet location alias for the supplied
+ * packet location name.
+ *
+ * The file containing the packet location definitions is automatically
+ * re-read if its modification time has changed since the last call.
+ *
+ * @return 0 on success or a negative error code.
+ * @retval NLE_PKTLOC_FILE Unable to open packet location file.
+ * @retval NLE_OBJ_NOTFOUND No matching packet location alias found.
+ */
+int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result)
+{
+	struct rtnl_pktloc *loc;
+	int hash, err;
+
+	if ((err = read_pktlocs()) < 0)
+		return err;
+
+	hash = pktloc_hash(name);
+	nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) {
+		if (!strcasecmp(loc->name, name)) {
+			*result = loc;
+			return 0;
+		}
+	}
+
+	return -NLE_OBJ_NOTFOUND;
+}
+
+static int __init pktloc_init(void)
+{
+	int i;
+
+	for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
+		nl_init_list_head(&pktloc_name_ht[i]);
+	
+	return 0;
+}
diff --git a/lib/route/pktloc_grammar.l b/lib/route/pktloc_grammar.l
new file mode 100644
index 0000000..f710430
--- /dev/null
+++ b/lib/route/pktloc_grammar.l
@@ -0,0 +1,42 @@
+%{
+ #include <netlink-local.h>
+ #include <netlink-tc.h>
+ #include <netlink/netlink.h>
+ #include <netlink/utils.h>
+ #include <netlink/route/pktloc.h>
+ #include "pktloc_syntax.h"
+%}
+
+%option 8bit
+%option reentrant
+%option warn
+%option noyywrap
+%option nounput
+%option bison-bridge
+%option bison-locations
+%option prefix="pktloc_"
+
+%%
+
+[ \t\r\n]+
+
+"#".*
+
+[[:digit:]]+		|
+0[xX][[:xdigit:]]+	{
+				yylval->i = strtoul(yytext, NULL, 0);
+				return NUMBER;
+			}
+
+"+"			{ return yylval->i = yytext[0]; }
+
+[lL][iI][nN][kK]	{ yylval->i = TCF_LAYER_LINK; return LAYER; }
+[nN][eE][tT]		{ yylval->i = TCF_LAYER_NETWORK; return LAYER; }
+[tT][cC][pP]		{ yylval->i = TCF_LAYER_TRANSPORT; return LAYER; }
+
+[^ \t\r\n+]+		{
+				yylval->s = strdup(yytext);
+				if (yylval->s == NULL)
+					return ERROR;
+				return NAME;
+			}
diff --git a/lib/route/pktloc_syntax.y b/lib/route/pktloc_syntax.y
new file mode 100644
index 0000000..05d609a
--- /dev/null
+++ b/lib/route/pktloc_syntax.y
@@ -0,0 +1,108 @@
+%{
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/pktloc.h>
+%}
+
+%locations
+%error-verbose
+%define api.pure
+%name-prefix "pktloc_"
+
+%parse-param {void *scanner}
+%lex-param {void *scanner}
+
+%union {
+	struct rtnl_pktloc *l;
+	uint32_t i;
+	char *s;
+}
+
+%{
+extern int pktloc_lex(YYSTYPE *, YYLTYPE *, void *);
+extern void rtnl_pktloc_add(struct rtnl_pktloc *);
+
+static void yyerror(YYLTYPE *locp, void *scanner, const char *msg)
+{
+	/* FIXME */
+}
+%}
+
+%token <i> ERROR NUMBER LAYER
+%token <s> NAME
+
+%type <i> mask layer
+%type <l> location
+
+%destructor { free($$); } NAME
+
+%start input
+
+%%
+
+input:
+	def
+		{ }
+	;
+
+def:
+	/* empty */
+		{ }
+	| location def
+		{ }
+	;
+
+location:
+	NAME NAME layer NUMBER mask
+		{
+			struct rtnl_pktloc *loc;
+
+			if (!(loc = calloc(1, sizeof(*loc)))) {
+				/* FIXME */
+			}
+
+			if (!strcasecmp($2, "u8"))
+				loc->align = TCF_EM_ALIGN_U8;
+			else if (!strcasecmp($2, "h8")) {
+				loc->align = TCF_EM_ALIGN_U8;
+				loc->flags = TCF_EM_CMP_TRANS;
+			} else if (!strcasecmp($2, "u16"))
+				loc->align = TCF_EM_ALIGN_U16;
+			else if (!strcasecmp($2, "h16")) {
+				loc->align = TCF_EM_ALIGN_U16;
+				loc->flags = TCF_EM_CMP_TRANS;
+			} else if (!strcasecmp($2, "u32"))
+				loc->align = TCF_EM_ALIGN_U32;
+			else if (!strcasecmp($2, "h32")) {
+				loc->align = TCF_EM_ALIGN_U32;
+				loc->flags = TCF_EM_CMP_TRANS;
+			}
+			
+			free($2);
+
+			loc->name = $1;
+			loc->layer = $3;
+			loc->offset = $4;
+			loc->mask = $5;
+
+			rtnl_pktloc_add(loc);
+
+			$$ = loc;
+		}
+	;
+
+layer:
+	/* empty */
+		{ $$ = TCF_LAYER_NETWORK; }
+	| LAYER '+' 
+		{ $$ = $1; }
+	;
+
+mask:
+	/* empty */
+		{ $$ = 0; }
+	| NUMBER
+		{ $$ = $1; }
+	;
diff --git a/lib/route/qdisc.c b/lib/route/qdisc.c
index 8eda51b..cfeaf05 100644
--- a/lib/route/qdisc.c
+++ b/lib/route/qdisc.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -98,13 +98,13 @@
 static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
 			    struct nlmsghdr *n, struct nl_parser_param *pp)
 {
-	int err = -ENOMEM;
+	int err;
 	struct rtnl_qdisc *qdisc;
 	struct rtnl_qdisc_ops *qops;
 
 	qdisc = rtnl_qdisc_alloc();
 	if (!qdisc) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 
@@ -122,25 +122,20 @@
 	}
 
 	err = pp->pp_cb((struct nl_object *) qdisc, pp);
-	if (err < 0)
-		goto errout_free;
-
-	err = P_ACCEPT;
-
 errout_free:
 	rtnl_qdisc_put(qdisc);
 errout:
 	return err;
 }
 
-static int qdisc_request_update(struct nl_cache *c, struct nl_handle *h)
+static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
 {
 	struct tcmsg tchdr = {
 		.tcm_family = AF_UNSPEC,
 		.tcm_ifindex = c->c_iarg1,
 	};
 
-	return nl_send_simple(h, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
+	return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
 			      sizeof(tchdr));
 }
 
@@ -149,15 +144,15 @@
  * @{
  */
 
-static struct nl_msg *qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags)
+static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
+		       struct nl_msg **result)
 {
 	struct rtnl_qdisc_ops *qops;
-	struct nl_msg *msg;
 	int err;
 
-	msg = tca_build_msg((struct rtnl_tca *) qdisc, type, flags);
-	if (!msg)
-		goto errout;
+	err = tca_build_msg((struct rtnl_tca *) qdisc, type, flags, result);
+	if (err < 0)
+		return err;
 
 	qops = rtnl_qdisc_lookup_ops(qdisc);
 	if (qops && qops->qo_get_opts) {
@@ -165,24 +160,33 @@
 		
 		opts = qops->qo_get_opts(qdisc);
 		if (opts) {
-			err = nla_put_nested(msg, TCA_OPTIONS, opts);
+			err = nla_put_nested(*result, TCA_OPTIONS, opts);
 			nlmsg_free(opts);
 			if (err < 0)
 				goto errout;
 		}
 	}
+	/* Some qdiscs don't accept properly nested messages (e.g. netem). To
+	 * accomodate for this, they can complete the message themselves.
+	 */		
+	else if (qops && qops->qo_build_msg) {
+		err = qops->qo_build_msg(qdisc, *result);
+		if (err < 0)
+			goto errout;
+	}
 
-	return msg;
+	return 0;
 errout:
-	nlmsg_free(msg);
+	nlmsg_free(*result);
 
-	return NULL;
+	return err;
 }
 
 /**
  * Build a netlink message to add a new qdisc
  * @arg qdisc		qdisc to add 
  * @arg flags		additional netlink message flags
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting an addition of a qdisc.
  * The netlink message header isn't fully equipped with all relevant
@@ -192,23 +196,17 @@
  * Common message flags used:
  *  - NLM_F_REPLACE - replace a potential existing qdisc
  *
- * @return New netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc,
-					    int flags)
+int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
+				 struct nl_msg **result)
 {
-	struct nl_msg *msg;
-
-	msg = qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags);
-	if (!msg)
-		nl_errno(ENOMEM);
-
-	return msg;
+	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
 }
 
 /**
  * Add a new qdisc
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg qdisc		qdisc to delete
  * @arg flags		additional netlink message flags
  *
@@ -221,22 +219,21 @@
  *
  * @return 0 on success or a negative error code
  */
-int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
+int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
 		   int flags)
 {
 	struct nl_msg *msg;
 	int err;
 
-	msg = rtnl_qdisc_build_add_request(qdisc, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -250,23 +247,25 @@
  * Build a netlink message to change attributes of a existing qdisc
  * @arg qdisc		qdisc to change
  * @arg new		new qdisc attributes
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting an change of qdisc
  * attributes. The netlink message header isn't fully equipped
  * with all relevant fields and must be sent out via
  * nl_send_auto_complete() or supplemented as needed. 
  *
- * @return New netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
-					       struct rtnl_qdisc *new)
+int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
+				    struct rtnl_qdisc *new,
+				    struct nl_msg **result)
 {
-	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE);
+	return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
 }
 
 /**
  * Change attributes of a qdisc
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg qdisc		qdisc to change
  * @arg new		new qdisc attributes
  *
@@ -276,22 +275,21 @@
  *
  * @return 0 on success or a negative error code
  */
-int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
+int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
 		      struct rtnl_qdisc *new)
 {
 	struct nl_msg *msg;
 	int err;
 
-	msg = rtnl_qdisc_build_change_request(qdisc, new);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -304,15 +302,17 @@
 /**
  * Build a netlink request message to delete a qdisc
  * @arg qdisc		qdisc to delete
+ * @arg result		Pointer to store resulting message.
  *
  * Builds a new netlink message requesting a deletion of a qdisc.
  * The netlink message header isn't fully equipped with all relevant
  * fields and must thus be sent out via nl_send_auto_complete()
  * or supplemented as needed.
  *
- * @return New netlink message
+ * @return 0 on success or a negative error code.
  */
-struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc)
+int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
+				    struct nl_msg **result)
 {
 	struct nl_msg *msg;
 	struct tcmsg tchdr;
@@ -323,20 +323,24 @@
 
 	msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
 	if (!msg)
-		return NULL;
+		return -NLE_NOMEM;
 
-	tchdr.tcm_family = AF_UNSPEC,
-	tchdr.tcm_handle = qdisc->q_handle,
-	tchdr.tcm_parent = qdisc->q_parent,
-	tchdr.tcm_ifindex = qdisc->q_ifindex,
-	nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO);
+	tchdr.tcm_family = AF_UNSPEC;
+	tchdr.tcm_handle = qdisc->q_handle;
+	tchdr.tcm_parent = qdisc->q_parent;
+	tchdr.tcm_ifindex = qdisc->q_ifindex;
+	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
+		nlmsg_free(msg);
+		return -NLE_MSGSIZE;
+	}
 
-	return msg;
+	*result = msg;
+	return 0;
 }
 
 /**
  * Delete a qdisc
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg qdisc		qdisc to delete
  *
  * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
@@ -345,21 +349,20 @@
  *
  * @return 0 on success or a negative error code
  */
-int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc)
+int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
 {
 	struct nl_msg *msg;
 	int err;
 
-	msg = rtnl_qdisc_build_delete_request(qdisc);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -372,29 +375,17 @@
 /**
  * Build a qdisc cache including all qdiscs currently configured in
  * the kernel
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
+ * @arg result		Pointer to store resulting message.
  *
  * Allocates a new cache, initializes it properly and updates it to
  * include all qdiscs currently configured in the kernel.
  *
- * @note The caller is responsible for destroying and freeing the
- *       cache after using it.
- * @return The cache or NULL if an error has occured.
+ * @return 0 on success or a negative error code.
  */
-struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle)
+int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
 {
-	struct nl_cache * cache;
-	
-	cache = nl_cache_alloc(&rtnl_qdisc_ops);
-	if (cache == NULL)
-		return NULL;
-
-	if (handle && nl_cache_refill(handle, cache) < 0) {
-		nl_cache_free(cache);
-		return NULL;
-	}
-
-	return cache;
+	return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
 }
 
 /**
diff --git a/lib/route/qdisc_api.c b/lib/route/qdisc_api.c
index ef4d07a..089f212 100644
--- a/lib/route/qdisc_api.c
+++ b/lib/route/qdisc_api.c
@@ -46,7 +46,7 @@
 
 	for (op = &qdisc_ops_list; (o = *op) != NULL; op = &o->qo_next)
 		if (!strcasecmp(qops->qo_kind, o->qo_kind))
-			return nl_errno(EEXIST);
+			return -NLE_EXIST;
 
 	qops->qo_next = NULL;
 	*op = qops;
@@ -67,7 +67,7 @@
 			break;
 
 	if (!o)
-		return nl_errno(ENOENT);
+		return -NLE_OBJ_NOTFOUND;
 
 	*op = qops->qo_next;
 
diff --git a/lib/route/qdisc_obj.c b/lib/route/qdisc_obj.c
index bbbb954..dc52ae8 100644
--- a/lib/route/qdisc_obj.c
+++ b/lib/route/qdisc_obj.c
@@ -56,54 +56,49 @@
 	return err;
 }
 
-static int qdisc_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
+static void qdisc_dump_line(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj;
 	struct rtnl_qdisc_ops *qops;
-
-	int line = tca_dump_brief((struct rtnl_tca *) qdisc, "qdisc", p, 0);
+	
+	tca_dump_line((struct rtnl_tca *) qdisc, "qdisc", p);
 
 	qops = rtnl_qdisc_lookup_ops(qdisc);
-	if (qops && qops->qo_dump[NL_DUMP_BRIEF])
-		line = qops->qo_dump[NL_DUMP_BRIEF](qdisc, p, line);
+	if (qops && qops->qo_dump[NL_DUMP_LINE])
+		qops->qo_dump[NL_DUMP_LINE](qdisc, p);
 
-	dp_dump(p, "\n");
-
-	return line;
+	nl_dump(p, "\n");
 }
 
-static int qdisc_dump_full(struct nl_object *arg, struct nl_dump_params *p)
+static void qdisc_dump_details(struct nl_object *arg, struct nl_dump_params *p)
 {
 	struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg;
 	struct rtnl_qdisc_ops *qops;
 
-	int line = qdisc_dump_brief(arg, p);
+	qdisc_dump_line(arg, p);
 
-	line = tca_dump_full((struct rtnl_tca *) qdisc, p, line);
-	dp_dump(p, "refcnt %u ", qdisc->q_info);
+	tca_dump_details((struct rtnl_tca *) qdisc, p);
+	nl_dump(p, "refcnt %u ", qdisc->q_info);
 
 	qops = rtnl_qdisc_lookup_ops(qdisc);
-	if (qops && qops->qo_dump[NL_DUMP_FULL])
-		line = qops->qo_dump[NL_DUMP_FULL](qdisc, p, line);
+	if (qops && qops->qo_dump[NL_DUMP_DETAILS])
+		qops->qo_dump[NL_DUMP_DETAILS](qdisc, p);
 
-	dp_dump(p, "\n");
-	return line;
+	nl_dump(p, "\n");
 }
 
-static int qdisc_dump_stats(struct nl_object *arg, struct nl_dump_params *p)
+static void qdisc_dump_stats(struct nl_object *arg, struct nl_dump_params *p)
 {
 	struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg;
 	struct rtnl_qdisc_ops *qops;
 
-	int line = qdisc_dump_full(arg, p);
-	line = tca_dump_stats((struct rtnl_tca *) qdisc, p, line );
-	dp_dump(p, "\n");
+	qdisc_dump_details(arg, p);
+	tca_dump_stats((struct rtnl_tca *) qdisc, p);
+	nl_dump(p, "\n");
 
 	qops = rtnl_qdisc_lookup_ops(qdisc);
 	if (qops && qops->qo_dump[NL_DUMP_STATS])
-		line = qops->qo_dump[NL_DUMP_STATS](qdisc, p, line);
-
-	return line;
+		qops->qo_dump[NL_DUMP_STATS](qdisc, p);
 }
 
 /**
@@ -263,9 +258,11 @@
 	.oo_size		= sizeof(struct rtnl_qdisc),
 	.oo_free_data		= qdisc_free_data,
 	.oo_clone		= qdisc_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= qdisc_dump_brief,
-	.oo_dump[NL_DUMP_FULL]	= qdisc_dump_full,
-	.oo_dump[NL_DUMP_STATS]	= qdisc_dump_stats,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= qdisc_dump_line,
+	    [NL_DUMP_DETAILS]	= qdisc_dump_details,
+	    [NL_DUMP_STATS]	= qdisc_dump_stats,
+	},
 	.oo_compare		= tca_compare,
 	.oo_id_attrs		= (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
 };
diff --git a/lib/route/route.c b/lib/route/route.c
index 0644bd7..c85c225 100644
--- a/lib/route/route.c
+++ b/lib/route/route.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -27,199 +27,31 @@
 
 static struct nl_cache_ops rtnl_route_ops;
 
-static struct nla_policy route_policy[RTA_MAX+1] = {
-	[RTA_IIF]	= { .type = NLA_STRING,
-			    .maxlen = IFNAMSIZ, },
-	[RTA_OIF]	= { .type = NLA_U32 },
-	[RTA_PRIORITY]	= { .type = NLA_U32 },
-	[RTA_FLOW]	= { .type = NLA_U32 },
-	[RTA_MP_ALGO]	= { .type = NLA_U32 },
-	[RTA_CACHEINFO]	= { .minlen = sizeof(struct rta_cacheinfo) },
-	[RTA_METRICS]	= { .type = NLA_NESTED },
-	[RTA_MULTIPATH]	= { .type = NLA_NESTED },
-};
-
-static void copy_cacheinfo_into_route(struct rta_cacheinfo *ci,
-				      struct rtnl_route *route)
-{
-	struct rtnl_rtcacheinfo nci = {
-		.rtci_clntref  = ci->rta_clntref,
-		.rtci_last_use = ci->rta_lastuse,
-		.rtci_expires  = ci->rta_expires,
-		.rtci_error    = ci->rta_error,
-		.rtci_used     = ci->rta_used,
-		.rtci_id       = ci->rta_id,
-		.rtci_ts       = ci->rta_ts,
-		.rtci_tsage    = ci->rta_tsage,
-	};
-
-	rtnl_route_set_cacheinfo(route, &nci);
-}
-
 static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
 			    struct nlmsghdr *nlh, struct nl_parser_param *pp)
 {
-	struct rtmsg *rtm;
 	struct rtnl_route *route;
-	struct nlattr *tb[RTA_MAX + 1];
-	struct nl_addr *src = NULL, *dst = NULL, *addr;
 	int err;
 
-	route = rtnl_route_alloc();
-	if (!route) {
-		err = nl_errno(ENOMEM);
-		goto errout;
-	}
-
-	route->ce_msgtype = nlh->nlmsg_type;
-
-	err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX,
-			  route_policy);
-	if (err < 0)
-		goto errout;
-
-	rtm = nlmsg_data(nlh);
-	rtnl_route_set_family(route, rtm->rtm_family);
-	rtnl_route_set_tos(route, rtm->rtm_tos);
-	rtnl_route_set_table(route, rtm->rtm_table);
-	rtnl_route_set_type(route, rtm->rtm_type);
-	rtnl_route_set_scope(route, rtm->rtm_scope);
-	rtnl_route_set_protocol(route, rtm->rtm_protocol);
-	rtnl_route_set_flags(route, rtm->rtm_flags);
-
-	if (tb[RTA_DST]) {
-		dst = nla_get_addr(tb[RTA_DST], rtm->rtm_family);
-		if (dst == NULL)
-			goto errout_errno;
-	} else {
-		dst = nl_addr_alloc(0);
-		nl_addr_set_family(dst, rtm->rtm_family);
-	}
-
-	nl_addr_set_prefixlen(dst, rtm->rtm_dst_len);
-	err = rtnl_route_set_dst(route, dst);
-	if (err < 0)
-		goto errout;
-
-	nl_addr_put(dst);
-
-	if (tb[RTA_SRC]) {
-		src = nla_get_addr(tb[RTA_SRC], rtm->rtm_family);
-		if (src == NULL)
-			goto errout_errno;
-	} else if (rtm->rtm_src_len)
-		src = nl_addr_alloc(0);
-
-	if (src) {
-		nl_addr_set_prefixlen(src, rtm->rtm_src_len);
-		rtnl_route_set_src(route, src);
-		nl_addr_put(src);
-	}
-
-	if (tb[RTA_IIF])
-		rtnl_route_set_iif(route, nla_get_string(tb[RTA_IIF]));
-
-	if (tb[RTA_OIF])
-		rtnl_route_set_oif(route, nla_get_u32(tb[RTA_OIF]));
-
-	if (tb[RTA_GATEWAY]) {
-		addr = nla_get_addr(tb[RTA_GATEWAY], route->rt_family);
-		if (addr == NULL)
-			goto errout_errno;
-		rtnl_route_set_gateway(route, addr);
-		nl_addr_put(addr);
-	}
-
-	if (tb[RTA_PRIORITY])
-		rtnl_route_set_prio(route, nla_get_u32(tb[RTA_PRIORITY]));
-
-	if (tb[RTA_PREFSRC]) {
-		addr = nla_get_addr(tb[RTA_PREFSRC], route->rt_family);
-		if (addr == NULL)
-			goto errout_errno;
-		rtnl_route_set_pref_src(route, addr);
-		nl_addr_put(addr);
-	}
-
-	if (tb[RTA_METRICS]) {
-		struct nlattr *mtb[RTAX_MAX + 1];
-		int i;
-
-		err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL);
-		if (err < 0)
-			goto errout;
-
-		for (i = 1; i <= RTAX_MAX; i++) {
-			if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) {
-				uint32_t m = nla_get_u32(mtb[i]);
-				if (rtnl_route_set_metric(route, i, m) < 0)
-					goto errout_errno;
-			}
-		}
-	}
-
-	if (tb[RTA_MULTIPATH]) {
-		struct rtnl_nexthop *nh;
-		struct rtnexthop *rtnh = nla_data(tb[RTA_MULTIPATH]);
-		size_t tlen = nla_len(tb[RTA_MULTIPATH]);
-
-		while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) {
-			nh = rtnl_route_nh_alloc();
-			if (!nh)
-				goto errout;
-
-			rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops);
-			rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex);
-			rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags);
-
-			if (rtnh->rtnh_len > sizeof(*rtnh)) {
-				struct nlattr *ntb[RTA_MAX + 1];
-				nla_parse(ntb, RTA_MAX, (struct nlattr *)
-					  RTNH_DATA(rtnh),
-					  rtnh->rtnh_len - sizeof(*rtnh),
-					  route_policy);
-
-				if (ntb[RTA_GATEWAY]) {
-					nh->rtnh_gateway = nla_get_addr(
-							ntb[RTA_GATEWAY],
-							route->rt_family);
-					nh->rtnh_mask = NEXTHOP_HAS_GATEWAY;
-				}
-			}
-
-			rtnl_route_add_nexthop(route, nh);
-			tlen -= RTNH_ALIGN(rtnh->rtnh_len);
-			rtnh = RTNH_NEXT(rtnh);
-		}
-	}
-
-	if (tb[RTA_FLOW])
-		rtnl_route_set_realms(route, nla_get_u32(tb[RTA_FLOW]));
-
-	if (tb[RTA_CACHEINFO])
-		copy_cacheinfo_into_route(nla_data(tb[RTA_CACHEINFO]), route);
-
-	if (tb[RTA_MP_ALGO])
-		rtnl_route_set_mp_algo(route, nla_get_u32(tb[RTA_MP_ALGO]));
+	if ((err = rtnl_route_parse(nlh, &route)) < 0)
+		return err;
 
 	err = pp->pp_cb((struct nl_object *) route, pp);
-	if (err < 0)
-		goto errout;
 
-	err = P_ACCEPT;
-
-errout:
 	rtnl_route_put(route);
 	return err;
-
-errout_errno:
-	err = nl_get_errno();
-	goto errout;
 }
 
-static int route_request_update(struct nl_cache *c, struct nl_handle *h)
+static int route_request_update(struct nl_cache *c, struct nl_sock *h)
 {
-	return nl_rtgen_request(h, RTM_GETROUTE, AF_UNSPEC, NLM_F_DUMP);
+	struct rtmsg rhdr = {
+		.rtm_family = c->c_iarg1,
+	};
+
+	if (c->c_iarg2 & ROUTE_CACHE_CONTENT)
+		rhdr.rtm_flags |= RTM_F_CLONED;
+
+	return nl_send_simple(h, RTM_GETROUTE, NLM_F_DUMP, &rhdr, sizeof(rhdr));
 }
 
 /**
@@ -229,7 +61,9 @@
 
 /**
  * Build a route cache holding all routes currently configured in the kernel
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
+ * @arg family		Address family of routes to cover or AF_UNSPEC
+ * @arg flags		Flags
  *
  * Allocates a new cache, initializes it properly and updates it to
  * contain all routes currently configured in the kernel.
@@ -238,20 +72,25 @@
  *       cache after using it.
  * @return The cache or NULL if an error has occured.
  */
-struct nl_cache *rtnl_route_alloc_cache(struct nl_handle *handle)
+int rtnl_route_alloc_cache(struct nl_sock *sk, int family, int flags,
+			   struct nl_cache **result)
 {
 	struct nl_cache *cache;
+	int err;
 
-	cache = nl_cache_alloc(&rtnl_route_ops);
-	if (!cache)
-		return NULL;
+	if (!(cache = nl_cache_alloc(&rtnl_route_ops)))
+		return -NLE_NOMEM;
 
-	if (handle && nl_cache_refill(handle, cache) < 0) {
+	cache->c_iarg1 = family;
+	cache->c_iarg2 = flags;
+
+	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
 		free(cache);
-		return NULL;
+		return err;
 	}
 
-	return cache;
+	*result = cache;
+	return 0;
 }
 
 /** @} */
@@ -261,152 +100,67 @@
  * @{
  */
 
-static struct nl_msg *build_route_msg(struct rtnl_route *tmpl, int cmd,
-				      int flags)
-{
-	struct nl_msg *msg;
-	struct nl_addr *addr;
-	int scope, i, oif, nmetrics = 0;
-	struct nlattr *metrics;
-	struct rtmsg rtmsg = {
-		.rtm_family = rtnl_route_get_family(tmpl),
-		.rtm_dst_len = rtnl_route_get_dst_len(tmpl),
-		.rtm_src_len = rtnl_route_get_src_len(tmpl),
-		.rtm_tos = rtnl_route_get_tos(tmpl),
-		.rtm_table = rtnl_route_get_table(tmpl),
-		.rtm_type = rtnl_route_get_type(tmpl),
-		.rtm_protocol = rtnl_route_get_protocol(tmpl),
-		.rtm_flags = rtnl_route_get_flags(tmpl),
-	};
-
-	if (rtmsg.rtm_family == AF_UNSPEC) {
-		nl_error(EINVAL, "Cannot build route message, address " \
-				 "family is unknown.");
-		return NULL;
-	}
-
-	scope = rtnl_route_get_scope(tmpl);
-	if (scope == RT_SCOPE_NOWHERE) {
-		if (rtmsg.rtm_type == RTN_LOCAL)
-			scope = RT_SCOPE_HOST;
-		else {
-			/* XXX Change to UNIVERSE if gw || nexthops */
-			scope = RT_SCOPE_LINK;
-		}
-	}
-
-	rtmsg.rtm_scope = scope;
-
-	msg = nlmsg_alloc_simple(cmd, flags);
-	if (msg == NULL)
-		return NULL;
-
-	if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0)
-		goto nla_put_failure;
-
-	addr = rtnl_route_get_dst(tmpl);
-	if (addr)
-		NLA_PUT_ADDR(msg, RTA_DST, addr);
-
-	addr = rtnl_route_get_src(tmpl);
-	if (addr)
-		NLA_PUT_ADDR(msg, RTA_SRC, addr);
-
-	addr = rtnl_route_get_gateway(tmpl);
-	if (addr)
-		NLA_PUT_ADDR(msg, RTA_GATEWAY, addr);
-
-	addr = rtnl_route_get_pref_src(tmpl);
-	if (addr)
-		NLA_PUT_ADDR(msg, RTA_PREFSRC, addr);
-
-	NLA_PUT_U32(msg, RTA_PRIORITY, rtnl_route_get_prio(tmpl));
-
-	oif = rtnl_route_get_oif(tmpl);
-	if (oif != RTNL_LINK_NOT_FOUND)
-		NLA_PUT_U32(msg, RTA_OIF, oif);
-
-	for (i = 1; i <= RTAX_MAX; i++)
-		if (rtnl_route_get_metric(tmpl, i) != UINT_MAX)
-			nmetrics++;
-
-	if (nmetrics > 0) {
-		unsigned int val;
-
-		metrics = nla_nest_start(msg, RTA_METRICS);
-		if (metrics == NULL)
-			goto nla_put_failure;
-
-		for (i = 1; i <= RTAX_MAX; i++) {
-			val = rtnl_route_get_metric(tmpl, i);
-			if (val != UINT_MAX)
-				NLA_PUT_U32(msg, i, val);
-		}
-
-		nla_nest_end(msg, metrics);
-	}
-
-#if 0
-	RTA_IIF,
-	RTA_MULTIPATH,
-	RTA_PROTOINFO,
-	RTA_FLOW,
-	RTA_CACHEINFO,
-	RTA_SESSION,
-	RTA_MP_ALGO,
-#endif
-
-	return msg;
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return NULL;
-}
-
-struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags)
-{
-	return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags);
-}
-
-int rtnl_route_add(struct nl_handle *handle, struct rtnl_route *route,
-		   int flags)
+static int build_route_msg(struct rtnl_route *tmpl, int cmd, int flags,
+			   struct nl_msg **result)
 {
 	struct nl_msg *msg;
 	int err;
 
-	msg = rtnl_route_build_add_request(route, flags);
-	if (!msg)
-		return nl_get_errno();
+	if (!(msg = nlmsg_alloc_simple(cmd, flags)))
+		return -NLE_NOMEM;
 
-	err = nl_send_auto_complete(handle, msg);
-	nlmsg_free(msg);
-	if (err < 0)
+	if ((err = rtnl_route_build_msg(msg, tmpl)) < 0) {
+		nlmsg_free(msg);
 		return err;
+	}
 
-	return nl_wait_for_ack(handle);
+	*result = msg;
+	return 0;
 }
 
-struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags)
+int rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags,
+				 struct nl_msg **result)
 {
-	return build_route_msg(tmpl, RTM_DELROUTE, flags);
+	return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags,
+			       result);
 }
 
-int rtnl_route_del(struct nl_handle *handle, struct rtnl_route *route,
-		   int flags)
+int rtnl_route_add(struct nl_sock *sk, struct rtnl_route *route, int flags)
 {
 	struct nl_msg *msg;
 	int err;
 
-	msg = rtnl_route_build_del_request(route, flags);
-	if (!msg)
-		return nl_get_errno();
+	if ((err = rtnl_route_build_add_request(route, flags, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
 	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
+}
+
+int rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags,
+				 struct nl_msg **result)
+{
+	return build_route_msg(tmpl, RTM_DELROUTE, flags, result);
+}
+
+int rtnl_route_delete(struct nl_sock *sk, struct rtnl_route *route, int flags)
+{
+	struct nl_msg *msg;
+	int err;
+
+	if ((err = rtnl_route_build_del_request(route, flags, &msg)) < 0)
+		return err;
+
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(sk);
 }
 
 /** @} */
diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c
index 78e7712..7f26bfd 100644
--- a/lib/route/route_obj.c
+++ b/lib/route/route_obj.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -20,14 +20,11 @@
  * routing table                                  RT_TABLE_MAIN
  * scope                                          RT_SCOPE_NOWHERE
  * tos                                            0
- * realms                                         0
  * protocol                                       RTPROT_STATIC
  * prio                                           0
  * family                                         AF_UNSPEC
  * type                                           RTN_UNICAST
- * oif                                            RTNL_LINK_NOT_FOUND
  * iif                                            NULL
- * mpalgo                                         IP_MP_ALG_NONE
  * @endcode
  *
  * @{
@@ -41,6 +38,7 @@
 #include <netlink/route/rtnl.h>
 #include <netlink/route/route.h>
 #include <netlink/route/link.h>
+#include <netlink/route/nexthop.h>
 
 /** @cond SKIP */
 #define ROUTE_ATTR_FAMILY    0x000001
@@ -61,15 +59,18 @@
 #define ROUTE_ATTR_MULTIPATH 0x008000
 #define ROUTE_ATTR_REALMS    0x010000
 #define ROUTE_ATTR_CACHEINFO 0x020000
-#define ROUTE_ATTR_MP_ALGO   0x040000
 /** @endcond */
 
-static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p);
-
 static void route_constructor(struct nl_object *c)
 {
 	struct rtnl_route *r = (struct rtnl_route *) c;
 
+	r->rt_family = AF_UNSPEC;
+	r->rt_scope = RT_SCOPE_NOWHERE;
+	r->rt_table = RT_TABLE_MAIN;
+	r->rt_protocol = RTPROT_STATIC;
+	r->rt_type = RTN_UNICAST;
+
 	nl_init_list_head(&r->rt_nexthops);
 }
 
@@ -83,11 +84,10 @@
 
 	nl_addr_put(r->rt_dst);
 	nl_addr_put(r->rt_src);
-	nl_addr_put(r->rt_gateway);
 	nl_addr_put(r->rt_pref_src);
 
 	nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) {
-		rtnl_route_remove_nexthop(nh);
+		rtnl_route_remove_nexthop(r, nh);
 		rtnl_route_nh_free(nh);
 	}
 }
@@ -100,366 +100,258 @@
 
 	if (src->rt_dst)
 		if (!(dst->rt_dst = nl_addr_clone(src->rt_dst)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	if (src->rt_src)
 		if (!(dst->rt_src = nl_addr_clone(src->rt_src)))
-			goto errout;
+			return -NLE_NOMEM;
 
-	if (src->rt_gateway)
-		if (!(dst->rt_gateway = nl_addr_clone(src->rt_gateway)))
-			goto errout;
-	
 	if (src->rt_pref_src)
 		if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	nl_init_list_head(&dst->rt_nexthops);
 	nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) {
 		new = rtnl_route_nh_clone(nh);
 		if (!new)
-			goto errout;
+			return -NLE_NOMEM;
 
 		rtnl_route_add_nexthop(dst, new);
 	}
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
-static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p)
+static void route_dump_line(struct nl_object *a, struct nl_dump_params *p)
 {
 	struct rtnl_route *r = (struct rtnl_route *) a;
 	struct nl_cache *link_cache;
+	int cache = 0, flags;
 	char buf[64];
 
 	link_cache = nl_cache_mngt_require("route/link");
 
+	if (r->rt_flags & RTM_F_CLONED)
+		cache = 1;
+
+	nl_dump_line(p, "%s ", nl_af2str(r->rt_family, buf, sizeof(buf)));
+
+	if (cache)
+		nl_dump(p, "cache ");
+
 	if (!(r->ce_mask & ROUTE_ATTR_DST) ||
 	    nl_addr_get_len(r->rt_dst) == 0)
-		dp_dump(p, "default ");
+		nl_dump(p, "default ");
 	else
-		dp_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf)));
+		nl_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf)));
 
-	if (r->ce_mask & ROUTE_ATTR_OIF) {
-		if (link_cache)
-			dp_dump(p, "dev %s ",
-				rtnl_link_i2name(link_cache, r->rt_oif,
-						 buf, sizeof(buf)));
-		else
-			dp_dump(p, "dev %d ", r->rt_oif);
+	if (r->ce_mask & ROUTE_ATTR_TABLE && !cache)
+		nl_dump(p, "table %s ",
+			rtnl_route_table2str(r->rt_table, buf, sizeof(buf)));
+
+	if (r->ce_mask & ROUTE_ATTR_TYPE)
+		nl_dump(p, "type %s ",
+			nl_rtntype2str(r->rt_type, buf, sizeof(buf)));
+
+	if (r->ce_mask & ROUTE_ATTR_TOS && r->rt_tos != 0)
+		nl_dump(p, "tos %#x ", r->rt_tos);
+
+	if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
+		struct rtnl_nexthop *nh;
+
+		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
+			p->dp_ivar = NH_DUMP_FROM_ONELINE;
+			rtnl_route_nh_dump(nh, p);
+		}
 	}
 
-	if (r->ce_mask & ROUTE_ATTR_GATEWAY)
-		dp_dump(p, "via %s ", nl_addr2str(r->rt_gateway, buf,
-						  sizeof(buf)));
-	else if (r->ce_mask & ROUTE_ATTR_MULTIPATH)
-		dp_dump(p, "via nexthops ");
+	flags = r->rt_flags & ~(RTM_F_CLONED);
+	if (r->ce_mask & ROUTE_ATTR_FLAGS && flags) {
 
-	if (r->ce_mask & ROUTE_ATTR_SCOPE)
-		dp_dump(p, "scope %s ",
-			rtnl_scope2str(r->rt_scope, buf, sizeof(buf)));
+		nl_dump(p, "<");
 
-	if (r->ce_mask & ROUTE_ATTR_FLAGS && r->rt_flags) {
-		int flags = r->rt_flags;
-
-		dp_dump(p, "<");
-		
 #define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \
-		flags &= ~RTNH_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); }
+		flags &= ~RTNH_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
 		PRINT_FLAG(DEAD);
 		PRINT_FLAG(ONLINK);
 		PRINT_FLAG(PERVASIVE);
 #undef PRINT_FLAG
 
 #define PRINT_FLAG(f) if (flags & RTM_F_##f) { \
-		flags &= ~RTM_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); }
+		flags &= ~RTM_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
 		PRINT_FLAG(NOTIFY);
-		PRINT_FLAG(CLONED);
 		PRINT_FLAG(EQUALIZE);
 		PRINT_FLAG(PREFIX);
 #undef PRINT_FLAG
 
-		dp_dump(p, ">");
+#define PRINT_FLAG(f) if (flags & RTCF_##f) { \
+		flags &= ~RTCF_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
+		PRINT_FLAG(NOTIFY);
+		PRINT_FLAG(REDIRECTED);
+		PRINT_FLAG(DOREDIRECT);
+		PRINT_FLAG(DIRECTSRC);
+		PRINT_FLAG(DNAT);
+		PRINT_FLAG(BROADCAST);
+		PRINT_FLAG(MULTICAST);
+		PRINT_FLAG(LOCAL);
+#undef PRINT_FLAG
+
+		nl_dump(p, ">");
 	}
 
-	dp_dump(p, "\n");
-
-	return 1;
+	nl_dump(p, "\n");
 }
 
-static int route_dump_full(struct nl_object *a, struct nl_dump_params *p)
+static void route_dump_details(struct nl_object *a, struct nl_dump_params *p)
 {
 	struct rtnl_route *r = (struct rtnl_route *) a;
 	struct nl_cache *link_cache;
 	char buf[128];
-	int i, line;
+	int i;
 
 	link_cache = nl_cache_mngt_require("route/link");
-	line = route_dump_brief(a, p);
+
+	route_dump_line(a, p);
+	nl_dump_line(p, "    ");
+
+	if (r->ce_mask & ROUTE_ATTR_PREF_SRC)
+		nl_dump(p, "preferred-src %s ",
+			nl_addr2str(r->rt_pref_src, buf, sizeof(buf)));
+
+	if (r->ce_mask & ROUTE_ATTR_SCOPE && r->rt_scope != RT_SCOPE_NOWHERE)
+		nl_dump(p, "scope %s ",
+			rtnl_scope2str(r->rt_scope, buf, sizeof(buf)));
+
+	if (r->ce_mask & ROUTE_ATTR_PRIO)
+		nl_dump(p, "priority %#x ", r->rt_prio);
+
+	if (r->ce_mask & ROUTE_ATTR_PROTOCOL)
+		nl_dump(p, "protocol %s ",
+			rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf)));
+
+	if (r->ce_mask & ROUTE_ATTR_IIF) {
+		if (link_cache) {
+			nl_dump(p, "iif %s ",
+				rtnl_link_i2name(link_cache, r->rt_iif,
+						 buf, sizeof(buf)));
+		} else
+			nl_dump(p, "iif %d ", r->rt_iif);
+	}
+
+	if (r->ce_mask & ROUTE_ATTR_SRC)
+		nl_dump(p, "src %s ", nl_addr2str(r->rt_src, buf, sizeof(buf)));
+
+	nl_dump(p, "\n");
 
 	if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
 		struct rtnl_nexthop *nh;
 
 		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
-			dp_dump_line(p, line++, "  via ");
-
-			if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY)
-				dp_dump(p, "%s ",
-					nl_addr2str(nh->rtnh_gateway,
-						    buf, sizeof(buf)));
-			if (link_cache) {
-				dp_dump(p, "dev %s ",
-					rtnl_link_i2name(link_cache,
-							 nh->rtnh_ifindex,
-							 buf, sizeof(buf)));
-			} else
-				dp_dump(p, "dev %d ", nh->rtnh_ifindex);
-
-			dp_dump(p, "weight %u <%s>\n", nh->rtnh_weight,
-				rtnl_route_nh_flags2str(nh->rtnh_flags,
-							buf, sizeof(buf)));
+			nl_dump_line(p, "    ");
+			p->dp_ivar = NH_DUMP_FROM_DETAILS;
+			rtnl_route_nh_dump(nh, p);
+			nl_dump(p, "\n");
 		}
 	}
 
-	dp_dump_line(p, line++, "  ");
-
-	if (r->ce_mask & ROUTE_ATTR_PREF_SRC)
-		dp_dump(p, "preferred-src %s ",
-			nl_addr2str(r->rt_pref_src, buf, sizeof(buf)));
-
-	if (r->ce_mask & ROUTE_ATTR_TABLE)
-		dp_dump(p, "table %s ",
-			rtnl_route_table2str(r->rt_table, buf, sizeof(buf)));
-
-	if (r->ce_mask & ROUTE_ATTR_TYPE)
-		dp_dump(p, "type %s ",
-			nl_rtntype2str(r->rt_type, buf, sizeof(buf)));
-
-	if (r->ce_mask & ROUTE_ATTR_PRIO)
-		dp_dump(p, "metric %#x ", r->rt_prio);
-
-	if (r->ce_mask & ROUTE_ATTR_FAMILY)
-		dp_dump(p, "family %s ",
-			nl_af2str(r->rt_family, buf, sizeof(buf)));
-
-	if (r->ce_mask & ROUTE_ATTR_PROTOCOL)
-		dp_dump(p, "protocol %s ",
-			rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf)));
-
-	dp_dump(p, "\n");
-
-	if ((r->ce_mask & (ROUTE_ATTR_IIF | ROUTE_ATTR_SRC | ROUTE_ATTR_TOS |
-			   ROUTE_ATTR_REALMS)) || 
-	    ((r->ce_mask & ROUTE_ATTR_CACHEINFO) &&
-	     r->rt_cacheinfo.rtci_error)) {
-		dp_dump_line(p, line++, "  ");
-
-		if (r->ce_mask & ROUTE_ATTR_IIF)
-			dp_dump(p, "iif %s ", r->rt_iif);
-
-		if (r->ce_mask & ROUTE_ATTR_SRC)
-			dp_dump(p, "src %s ",
-				nl_addr2str(r->rt_src, buf, sizeof(buf)));
-
-		if (r->ce_mask & ROUTE_ATTR_TOS)
-			dp_dump(p, "tos %#x ", r->rt_tos);
-
-		if (r->ce_mask & ROUTE_ATTR_REALMS)
-			dp_dump(p, "realm %04x:%04x ",
-				RTNL_REALM_FROM(r->rt_realms),
-				RTNL_REALM_TO(r->rt_realms));
-
-		if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) &&
-		    r->rt_cacheinfo.rtci_error)
-			dp_dump(p, "error %d (%s) ", r->rt_cacheinfo.rtci_error,
-				strerror(-r->rt_cacheinfo.rtci_error));
-
-		dp_dump(p, "\n");
+	if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) {
+		nl_dump_line(p, "    cacheinfo error %d (%s)\n",
+			r->rt_cacheinfo.rtci_error,
+			strerror(-r->rt_cacheinfo.rtci_error));
 	}
 
 	if (r->ce_mask & ROUTE_ATTR_METRICS) {
-		dp_dump_line(p, line++, "  ");
+		nl_dump_line(p, "    metrics [");
 		for (i = 0; i < RTAX_MAX; i++)
 			if (r->rt_metrics_mask & (1 << i))
-				dp_dump(p, "%s %u ",
+				nl_dump(p, "%s %u ",
 					rtnl_route_metric2str(i+1,
 							      buf, sizeof(buf)),
 					r->rt_metrics[i]);
-		dp_dump(p, "\n");
+		nl_dump(p, "]\n");
 	}
-
-	return line;
 }
 
-static int route_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_route *route = (struct rtnl_route *) obj;
-	int line;
 
-	line = route_dump_full(obj, p);
+	route_dump_details(obj, p);
 
 	if (route->ce_mask & ROUTE_ATTR_CACHEINFO) {
 		struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo;
-		dp_dump_line(p, line++, "  used %u refcnt %u ",
-			     ci->rtci_used, ci->rtci_clntref);
-		dp_dump_line(p, line++, "last-use %us expires %us\n",
+
+		nl_dump_line(p, "    used %u refcnt %u last-use %us "
+				"expires %us\n",
+			     ci->rtci_used, ci->rtci_clntref,
 			     ci->rtci_last_use / nl_get_hz(),
 			     ci->rtci_expires / nl_get_hz());
 	}
-
-	return line;
 }
 
-static int route_dump_xml(struct nl_object *obj, struct nl_dump_params *p)
+static void route_dump_env(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_route *route = (struct rtnl_route *) obj;
+	struct nl_cache *link_cache;
 	char buf[128];
-	int line = 0;
-	
-	dp_dump_line(p, line++, "<route>\n");
-	dp_dump_line(p, line++, "  <family>%s</family>\n",
+
+	link_cache = nl_cache_mngt_require("route/link");
+
+	nl_dump_line(p, "ROUTE_FAMILY=%s\n",
 		     nl_af2str(route->rt_family, buf, sizeof(buf)));
 
 	if (route->ce_mask & ROUTE_ATTR_DST)
-		dp_dump_line(p, line++, "  <dst>%s</dst>\n",
+		nl_dump_line(p, "ROUTE_DST=%s\n",
 			     nl_addr2str(route->rt_dst, buf, sizeof(buf)));
 
 	if (route->ce_mask & ROUTE_ATTR_SRC)
-		dp_dump_line(p, line++, "  <src>%s</src>\n",
+		nl_dump_line(p, "ROUTE_SRC=%s\n",
 			     nl_addr2str(route->rt_src, buf, sizeof(buf)));
 
-	if (route->ce_mask & ROUTE_ATTR_GATEWAY)
-		dp_dump_line(p, line++, "  <gateway>%s</gateway>\n",
-			     nl_addr2str(route->rt_gateway, buf, sizeof(buf)));
-
 	if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
-		dp_dump_line(p, line++, "  <prefsrc>%s</prefsrc>\n",
+		nl_dump_line(p, "ROUTE_PREFSRC=%s\n",
 			     nl_addr2str(route->rt_pref_src, buf, sizeof(buf)));
 
-	if (route->ce_mask & ROUTE_ATTR_IIF)
-		dp_dump_line(p, line++, "  <iif>%s</iif>\n", route->rt_iif);
-
-	if (route->ce_mask & ROUTE_ATTR_REALMS)
-		dp_dump_line(p, line++, "  <realms>%u</realms>\n",
-			     route->rt_realms);
+	if (route->ce_mask & ROUTE_ATTR_IIF) {
+		if (link_cache) {
+			nl_dump_line(p, "ROUTE_IIF=%s",
+				rtnl_link_i2name(link_cache, route->rt_iif,
+						 buf, sizeof(buf)));
+		} else
+			nl_dump_line(p, "ROUTE_IIF=%d", route->rt_iif);
+	}
 
 	if (route->ce_mask & ROUTE_ATTR_TOS)
-		dp_dump_line(p, line++, "  <tos>%u</tos>\n", route->rt_tos);
+		nl_dump_line(p, "ROUTE_TOS=%u\n", route->rt_tos);
 
 	if (route->ce_mask & ROUTE_ATTR_TABLE)
-		dp_dump_line(p, line++, "  <table>%u</table>\n",
+		nl_dump_line(p, "ROUTE_TABLE=%u\n",
 			     route->rt_table);
 
 	if (route->ce_mask & ROUTE_ATTR_SCOPE)
-		dp_dump_line(p, line++, "  <scope>%s</scope>\n",
+		nl_dump_line(p, "ROUTE_SCOPE=%s\n",
 			     rtnl_scope2str(route->rt_scope, buf, sizeof(buf)));
 
 	if (route->ce_mask & ROUTE_ATTR_PRIO)
-		dp_dump_line(p, line++, "  <metric>%u</metric>\n",
+		nl_dump_line(p, "ROUTE_PRIORITY=%u\n",
 			     route->rt_prio);
 
-	if (route->ce_mask & ROUTE_ATTR_OIF) {
-		struct nl_cache *link_cache;
-	
-		link_cache = nl_cache_mngt_require("route/link");
-		if (link_cache)
-			dp_dump_line(p, line++, "  <oif>%s</oif>\n",
-				     rtnl_link_i2name(link_cache,
-						      route->rt_oif,
-						      buf, sizeof(buf)));
-		else
-			dp_dump_line(p, line++, "  <oif>%u</oif>\n",
-				     route->rt_oif);
-	}
-
 	if (route->ce_mask & ROUTE_ATTR_TYPE)
-		dp_dump_line(p, line++, "  <type>%s</type>\n",
+		nl_dump_line(p, "ROUTE_TYPE=%s\n",
 			     nl_rtntype2str(route->rt_type, buf, sizeof(buf)));
 
-	dp_dump_line(p, line++, "</route>\n");
+	if (route->ce_mask & ROUTE_ATTR_MULTIPATH) {
+		struct rtnl_nexthop *nh;
+		int index = 1;
 
-#if 0
-	uint8_t			rt_protocol;
-	uint32_t		rt_flags;
-	uint32_t		rt_metrics[RTAX_MAX];
-	uint32_t		rt_metrics_mask;
-	struct rtnl_nexthop *	rt_nexthops;
-	struct rtnl_rtcacheinfo	rt_cacheinfo;
-	uint32_t		rt_mp_algo;
+		if (route->rt_nr_nh > 0)
+			nl_dump_line(p, "ROUTE_NR_NH=%u\n", route->rt_nr_nh);
 
-#endif
-
-	return line;
-}
-
-static int route_dump_env(struct nl_object *obj, struct nl_dump_params *p)
-{
-	struct rtnl_route *route = (struct rtnl_route *) obj;
-	char buf[128];
-	int line = 0;
-
-	dp_dump_line(p, line++, "ROUTE_FAMILY=%s\n",
-		     nl_af2str(route->rt_family, buf, sizeof(buf)));
-
-	if (route->ce_mask & ROUTE_ATTR_DST)
-		dp_dump_line(p, line++, "ROUTE_DST=%s\n",
-			     nl_addr2str(route->rt_dst, buf, sizeof(buf)));
-
-	if (route->ce_mask & ROUTE_ATTR_SRC)
-		dp_dump_line(p, line++, "ROUTE_SRC=%s\n",
-			     nl_addr2str(route->rt_src, buf, sizeof(buf)));
-
-	if (route->ce_mask & ROUTE_ATTR_GATEWAY)
-		dp_dump_line(p, line++, "ROUTE_GATEWAY=%s\n",
-			     nl_addr2str(route->rt_gateway, buf, sizeof(buf)));
-
-	if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
-		dp_dump_line(p, line++, "ROUTE_PREFSRC=%s\n",
-			     nl_addr2str(route->rt_pref_src, buf, sizeof(buf)));
-
-	if (route->ce_mask & ROUTE_ATTR_IIF)
-		dp_dump_line(p, line++, "ROUTE_IIF=%s\n", route->rt_iif);
-
-	if (route->ce_mask & ROUTE_ATTR_REALMS)
-		dp_dump_line(p, line++, "ROUTE_REALM=%u\n",
-			     route->rt_realms);
-
-	if (route->ce_mask & ROUTE_ATTR_TOS)
-		dp_dump_line(p, line++, "ROUTE_TOS=%u\n", route->rt_tos);
-
-	if (route->ce_mask & ROUTE_ATTR_TABLE)
-		dp_dump_line(p, line++, "ROUTE_TABLE=%u\n",
-			     route->rt_table);
-
-	if (route->ce_mask & ROUTE_ATTR_SCOPE)
-		dp_dump_line(p, line++, "ROUTE_SCOPE=%s\n",
-			     rtnl_scope2str(route->rt_scope, buf, sizeof(buf)));
-
-	if (route->ce_mask & ROUTE_ATTR_PRIO)
-		dp_dump_line(p, line++, "ROUTE_METRIC=%u\n",
-			     route->rt_prio);
-
-	if (route->ce_mask & ROUTE_ATTR_OIF) {
-		struct nl_cache *link_cache;
-
-		dp_dump_line(p, line++, "ROUTE_OIF_IFINDEX=%u\n",
-			     route->rt_oif);
-
-		link_cache = nl_cache_mngt_require("route/link");
-		if (link_cache)
-			dp_dump_line(p, line++, "ROUTE_OIF_IFNAME=%s\n",
-				     rtnl_link_i2name(link_cache,
-						      route->rt_oif,
-						      buf, sizeof(buf)));
+		nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
+			p->dp_ivar = index++;
+			rtnl_route_nh_dump(nh, p);
+		}
 	}
-
-	if (route->ce_mask & ROUTE_ATTR_TYPE)
-		dp_dump_line(p, line++, "ROUTE_TYPE=%s\n",
-			     nl_rtntype2str(route->rt_type, buf, sizeof(buf)));
-
-	return line;
 }
 
 static int route_compare(struct nl_object *_a, struct nl_object *_b,
@@ -467,7 +359,8 @@
 {
 	struct rtnl_route *a = (struct rtnl_route *) _a;
 	struct rtnl_route *b = (struct rtnl_route *) _b;
-	int diff = 0;
+	struct rtnl_nexthop *nh_a, *nh_b;
+	int i, diff = 0, found;
 
 #define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR)
 
@@ -477,29 +370,89 @@
 	diff |= ROUTE_DIFF(PROTOCOL,	a->rt_protocol != b->rt_protocol);
 	diff |= ROUTE_DIFF(SCOPE,	a->rt_scope != b->rt_scope);
 	diff |= ROUTE_DIFF(TYPE,	a->rt_type != b->rt_type);
-	diff |= ROUTE_DIFF(OIF,		a->rt_oif != b->rt_oif);
 	diff |= ROUTE_DIFF(PRIO,	a->rt_prio != b->rt_prio);
-	diff |= ROUTE_DIFF(REALMS,	a->rt_realms != b->rt_realms);
-	diff |= ROUTE_DIFF(MP_ALGO,	a->rt_mp_algo != b->rt_mp_algo);
 	diff |= ROUTE_DIFF(DST,		nl_addr_cmp(a->rt_dst, b->rt_dst));
 	diff |= ROUTE_DIFF(SRC,		nl_addr_cmp(a->rt_src, b->rt_src));
-	diff |= ROUTE_DIFF(IIF,		strcmp(a->rt_iif, b->rt_iif));
+	diff |= ROUTE_DIFF(IIF,		a->rt_iif != b->rt_iif);
 	diff |= ROUTE_DIFF(PREF_SRC,	nl_addr_cmp(a->rt_pref_src,
 						    b->rt_pref_src));
-	diff |= ROUTE_DIFF(GATEWAY,	nl_addr_cmp(a->rt_gateway,
-						    b->rt_gateway));
 
-	/* FIXME: Compare metrics, multipath config */
+	if (flags & LOOSE_COMPARISON) {
+		nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) {
+			found = 0;
+			nl_list_for_each_entry(nh_a, &a->rt_nexthops,
+					       rtnh_list) {
+				if (!rtnl_route_nh_compare(nh_a, nh_b,
+							nh_b->ce_mask, 1)) {
+					found = 1;
+					break;
+				}
+			}
 
-	if (flags & LOOSE_FLAG_COMPARISON)
+			if (!found)
+				goto nh_mismatch;
+		}
+
+		for (i = 0; i < RTAX_MAX - 1; i++) {
+			if (a->rt_metrics_mask & (1 << i) &&
+			    (!(b->rt_metrics_mask & (1 << i)) ||
+			     a->rt_metrics[i] != b->rt_metrics[i]))
+				ROUTE_DIFF(METRICS, 1);
+		}
+
 		diff |= ROUTE_DIFF(FLAGS,
 			  (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask);
-	else
-		diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags);
-	
-#undef ROUTE_DIFF
+	} else {
+		if (a->rt_nr_nh != a->rt_nr_nh)
+			goto nh_mismatch;
 
+		/* search for a dup in each nh of a */
+		nl_list_for_each_entry(nh_a, &a->rt_nexthops, rtnh_list) {
+			found = 0;
+			nl_list_for_each_entry(nh_b, &b->rt_nexthops,
+					       rtnh_list) {
+				if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0))
+					found = 1;
+					break;
+			}
+			if (!found)
+				goto nh_mismatch;
+		}
+
+		/* search for a dup in each nh of b, covers case where a has
+		 * dupes itself */
+		nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) {
+			found = 0;
+			nl_list_for_each_entry(nh_a, &a->rt_nexthops,
+					       rtnh_list) {
+				if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0))
+					found = 1;
+					break;
+			}
+			if (!found)
+				goto nh_mismatch;
+		}
+
+		for (i = 0; i < RTAX_MAX - 1; i++) {
+			if ((a->rt_metrics_mask & (1 << i)) ^
+			    (b->rt_metrics_mask & (1 << i)))
+				diff |= ROUTE_DIFF(METRICS, 1);
+			else
+				diff |= ROUTE_DIFF(METRICS,
+					a->rt_metrics[i] != b->rt_metrics[i]);
+		}
+
+		diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags);
+	}
+
+out:
 	return diff;
+
+nh_mismatch:
+	diff |= ROUTE_DIFF(MULTIPATH, 1);
+	goto out;
+
+#undef ROUTE_DIFF
 }
 
 static struct trans_tbl route_attrs[] = {
@@ -521,7 +474,6 @@
 	__ADD(ROUTE_ATTR_MULTIPATH, multipath)
 	__ADD(ROUTE_ATTR_REALMS, realms)
 	__ADD(ROUTE_ATTR_CACHEINFO, cacheinfo)
-	__ADD(ROUTE_ATTR_MP_ALGO, mp_algo)
 };
 
 static char *route_attrs2str(int attrs, char *buf, size_t len)
@@ -557,100 +509,82 @@
  * @{
  */
 
-void rtnl_route_set_table(struct rtnl_route *route, int table)
+void rtnl_route_set_table(struct rtnl_route *route, uint32_t table)
 {
 	route->rt_table = table;
 	route->ce_mask |= ROUTE_ATTR_TABLE;
 }
 
-int rtnl_route_get_table(struct rtnl_route *route)
+uint32_t rtnl_route_get_table(struct rtnl_route *route)
 {
-	if (route->ce_mask & ROUTE_ATTR_TABLE)
-		return route->rt_table;
-	else
-		return RT_TABLE_MAIN;
+	return route->rt_table;
 }
 
-void rtnl_route_set_scope(struct rtnl_route *route, int scope)
+void rtnl_route_set_scope(struct rtnl_route *route, uint8_t scope)
 {
 	route->rt_scope = scope;
 	route->ce_mask |= ROUTE_ATTR_SCOPE;
 }
 
-int rtnl_route_get_scope(struct rtnl_route *route)
+uint8_t rtnl_route_get_scope(struct rtnl_route *route)
 {
-	if (route->ce_mask & ROUTE_ATTR_SCOPE)
-		return route->rt_scope;
-	else
-		return RT_SCOPE_NOWHERE;
+	return route->rt_scope;
 }
 
-void rtnl_route_set_tos(struct rtnl_route *route, int tos)
+void rtnl_route_set_tos(struct rtnl_route *route, uint8_t tos)
 {
 	route->rt_tos = tos;
 	route->ce_mask |= ROUTE_ATTR_TOS;
 }
 
-int rtnl_route_get_tos(struct rtnl_route *route)
+uint8_t rtnl_route_get_tos(struct rtnl_route *route)
 {
 	return route->rt_tos;
 }
 
-void rtnl_route_set_realms(struct rtnl_route *route, realm_t realms)
+void rtnl_route_set_protocol(struct rtnl_route *route, uint8_t protocol)
 {
-	route->rt_realms = realms;
-	route->ce_mask |= ROUTE_ATTR_REALMS;
-}
-
-realm_t rtnl_route_get_realms(struct rtnl_route *route)
-{
-	return route->rt_realms;
-}
-
-void rtnl_route_set_protocol(struct rtnl_route *route, int proto)
-{
-	route->rt_protocol = proto;
+	route->rt_protocol = protocol;
 	route->ce_mask |= ROUTE_ATTR_PROTOCOL;
 }
 
-int rtnl_route_get_protocol(struct rtnl_route *route)
+uint8_t rtnl_route_get_protocol(struct rtnl_route *route)
 {
-	if (route->ce_mask & ROUTE_ATTR_PROTOCOL)
-		return route->rt_protocol;
-	else
-		return RTPROT_STATIC;
+	return route->rt_protocol;
 }
 
-void rtnl_route_set_prio(struct rtnl_route *route, int prio)
+void rtnl_route_set_priority(struct rtnl_route *route, uint32_t prio)
 {
 	route->rt_prio = prio;
 	route->ce_mask |= ROUTE_ATTR_PRIO;
 }
 
-int rtnl_route_get_prio(struct rtnl_route *route)
+uint32_t rtnl_route_get_priority(struct rtnl_route *route)
 {
 	return route->rt_prio;
 }
 
-void rtnl_route_set_family(struct rtnl_route *route, int family)
+int rtnl_route_set_family(struct rtnl_route *route, uint8_t family)
 {
+	if (family != AF_INET && family != AF_INET6 && family != AF_DECnet)
+		return -NLE_AF_NOSUPPORT;
+
 	route->rt_family = family;
 	route->ce_mask |= ROUTE_ATTR_FAMILY;
+
+	return 0;
 }
 
-int rtnl_route_get_family(struct rtnl_route *route)
+uint8_t rtnl_route_get_family(struct rtnl_route *route)
 {
-	if (route->ce_mask & ROUTE_ATTR_FAMILY)
-		return route->rt_family;
-	else
-		return AF_UNSPEC;
+	return route->rt_family;
 }
 
 int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr)
 {
 	if (route->ce_mask & ROUTE_ATTR_FAMILY) {
 		if (addr->a_family != route->rt_family)
-			return nl_error(EINVAL, "Address family mismatch");
+			return -NLE_AF_MISMATCH;
 	} else
 		route->rt_family = addr->a_family;
 
@@ -670,19 +604,14 @@
 	return route->rt_dst;
 }
 
-int rtnl_route_get_dst_len(struct rtnl_route *route)
-{
-	if (route->ce_mask & ROUTE_ATTR_DST)
-		return nl_addr_get_prefixlen(route->rt_dst);
-	else
-		return 0;
-}
-
 int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr)
 {
+	if (addr->a_family == AF_INET)
+		return -NLE_SRCRT_NOSUPPORT;
+
 	if (route->ce_mask & ROUTE_ATTR_FAMILY) {
 		if (addr->a_family != route->rt_family)
-			return nl_error(EINVAL, "Address family mismatch");
+			return -NLE_AF_MISMATCH;
 	} else
 		route->rt_family = addr->a_family;
 
@@ -701,66 +630,37 @@
 	return route->rt_src;
 }
 
-int rtnl_route_get_src_len(struct rtnl_route *route)
+int rtnl_route_set_type(struct rtnl_route *route, uint8_t type)
 {
-	if (route->ce_mask & ROUTE_ATTR_SRC)
-		return nl_addr_get_prefixlen(route->rt_src);
-	else
-		return 0;
-}
+	if (type > RTN_MAX)
+		return -NLE_RANGE;
 
-int rtnl_route_set_gateway(struct rtnl_route *route, struct nl_addr *addr)
-{
-	if (route->ce_mask & ROUTE_ATTR_FAMILY) {
-		if (addr->a_family != route->rt_family)
-			return nl_error(EINVAL, "Address family mismatch");
-	} else
-		route->rt_family = addr->a_family;
-
-	if (route->rt_gateway)
-		nl_addr_put(route->rt_gateway);
-
-	nl_addr_get(addr);
-	route->rt_gateway = addr;
-	route->ce_mask |= (ROUTE_ATTR_GATEWAY | ROUTE_ATTR_FAMILY);
+	route->rt_type = type;
+	route->ce_mask |= ROUTE_ATTR_TYPE;
 
 	return 0;
 }
 
-struct nl_addr *rtnl_route_get_gateway(struct rtnl_route *route)
+uint8_t rtnl_route_get_type(struct rtnl_route *route)
 {
-	return route->rt_gateway;
+	return route->rt_type;
 }
 
-void rtnl_route_set_type(struct rtnl_route *route, int type)
-{
-	route->rt_type = type;
-	route->ce_mask |= ROUTE_ATTR_TYPE;
-}
-
-int rtnl_route_get_type(struct rtnl_route *route)
-{
-	if (route->ce_mask & ROUTE_ATTR_TYPE)
-		return route->rt_type;
-	else
-		return RTN_UNICAST;
-}
-
-void rtnl_route_set_flags(struct rtnl_route *route, unsigned int flags)
+void rtnl_route_set_flags(struct rtnl_route *route, uint32_t flags)
 {
 	route->rt_flag_mask |= flags;
 	route->rt_flags |= flags;
 	route->ce_mask |= ROUTE_ATTR_FLAGS;
 }
 
-void rtnl_route_unset_flags(struct rtnl_route *route, unsigned int flags)
+void rtnl_route_unset_flags(struct rtnl_route *route, uint32_t flags)
 {
 	route->rt_flag_mask |= flags;
 	route->rt_flags &= ~flags;
 	route->ce_mask |= ROUTE_ATTR_FLAGS;
 }
 
-unsigned int rtnl_route_get_flags(struct rtnl_route *route)
+uint32_t rtnl_route_get_flags(struct rtnl_route *route)
 {
 	return route->rt_flags;
 }
@@ -768,11 +668,16 @@
 int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value)
 {
 	if (metric > RTAX_MAX || metric < 1)
-		return nl_error(EINVAL, "Metric out of range (1..%d)",
-		    RTAX_MAX);
+		return -NLE_RANGE;
 
 	route->rt_metrics[metric - 1] = value;
-	route->rt_metrics_mask |= (1 << (metric - 1));
+
+	if (!(route->rt_metrics_mask & (1 << (metric - 1)))) {
+		route->rt_nmetrics++;
+		route->rt_metrics_mask |= (1 << (metric - 1));
+	}
+
+	route->ce_mask |= ROUTE_ATTR_METRICS;
 
 	return 0;
 }
@@ -780,30 +685,35 @@
 int rtnl_route_unset_metric(struct rtnl_route *route, int metric)
 {
 	if (metric > RTAX_MAX || metric < 1)
-		return nl_error(EINVAL, "Metric out of range (1..%d)",
-		    RTAX_MAX);
+		return -NLE_RANGE;
 
-	route->rt_metrics_mask &= ~(1 << (metric - 1));
+	if (route->rt_metrics_mask & (1 << (metric - 1))) {
+		route->rt_nmetrics--;
+		route->rt_metrics_mask &= ~(1 << (metric - 1));
+	}
 
 	return 0;
 }
 
-unsigned int rtnl_route_get_metric(struct rtnl_route *route, int metric)
+int rtnl_route_get_metric(struct rtnl_route *route, int metric, uint32_t *value)
 {
 	if (metric > RTAX_MAX || metric < 1)
-		return UINT_MAX;
+		return -NLE_RANGE;
 
 	if (!(route->rt_metrics_mask & (1 << (metric - 1))))
-		return UINT_MAX;
+		return -NLE_OBJ_NOTFOUND;
 
-	return route->rt_metrics[metric - 1];
+	if (value)
+		*value = route->rt_metrics[metric - 1];
+
+	return 0;
 }
 
 int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr)
 {
 	if (route->ce_mask & ROUTE_ATTR_FAMILY) {
 		if (addr->a_family != route->rt_family)
-			return nl_error(EINVAL, "Address family mismatch");
+			return -NLE_AF_MISMATCH;
 	} else
 		route->rt_family = addr->a_family;
 
@@ -822,42 +732,27 @@
 	return route->rt_pref_src;
 }
 
-void rtnl_route_set_oif(struct rtnl_route *route, int ifindex)
+void rtnl_route_set_iif(struct rtnl_route *route, int ifindex)
 {
-	route->rt_oif = ifindex;
-	route->ce_mask |= ROUTE_ATTR_OIF;
-}
-
-int rtnl_route_get_oif(struct rtnl_route *route)
-{
-	if (route->ce_mask & ROUTE_ATTR_OIF)
-		return route->rt_oif;
-	else
-		return RTNL_LINK_NOT_FOUND;
-}
-
-void rtnl_route_set_iif(struct rtnl_route *route, const char *name)
-{
-	strncpy(route->rt_iif, name, sizeof(route->rt_iif) - 1);
+	route->rt_iif = ifindex;
 	route->ce_mask |= ROUTE_ATTR_IIF;
 }
 
-char *rtnl_route_get_iif(struct rtnl_route *route)
+int rtnl_route_get_iif(struct rtnl_route *route)
 {
-	if (route->ce_mask & ROUTE_ATTR_IIF)
-		return route->rt_iif;
-	else
-		return NULL;
+	return route->rt_iif;
 }
 
 void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
 {
 	nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops);
+	route->rt_nr_nh++;
 	route->ce_mask |= ROUTE_ATTR_MULTIPATH;
 }
 
-void rtnl_route_remove_nexthop(struct rtnl_nexthop *nh)
+void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
 {
+	route->rt_nr_nh--;
 	nl_list_del(&nh->rtnh_list);
 }
 
@@ -866,44 +761,437 @@
 	return &route->rt_nexthops;
 }
 
-void rtnl_route_set_cacheinfo(struct rtnl_route *route,
-			      struct rtnl_rtcacheinfo *ci)
+int rtnl_route_get_nnexthops(struct rtnl_route *route)
 {
-	memcpy(&route->rt_cacheinfo, ci, sizeof(*ci));
-	route->ce_mask |= ROUTE_ATTR_CACHEINFO;
+	return route->rt_nr_nh;
 }
 
-uint32_t rtnl_route_get_mp_algo(struct rtnl_route *route)
+void rtnl_route_foreach_nexthop(struct rtnl_route *r,
+                                void (*cb)(struct rtnl_nexthop *, void *),
+                                void *arg)
 {
-	if (route->ce_mask & ROUTE_ATTR_MP_ALGO)
-		return route->rt_mp_algo;
-	else
-		return IP_MP_ALG_NONE;
+	struct rtnl_nexthop *nh;
+    
+	if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
+		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
+                        cb(nh, arg);
+		}
+	}
 }
 
-void rtnl_route_set_mp_algo(struct rtnl_route *route, uint32_t algo)
+struct rtnl_nexthop *rtnl_route_nexthop_n(struct rtnl_route *r, int n)
 {
-	route->rt_mp_algo = algo;
-	route->ce_mask |= ROUTE_ATTR_MP_ALGO;
+	struct rtnl_nexthop *nh;
+	int i;
+    
+	if (r->ce_mask & ROUTE_ATTR_MULTIPATH && r->rt_nr_nh > n) {
+		i = 0;
+		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
+                        if (i == n) return nh;
+			i++;
+		}
+	}
+        return NULL;
 }
 
 /** @} */
 
+/**
+ * @name Utilities
+ * @{
+ */
+
+/**
+ * Guess scope of a route object.
+ * @arg route		Route object.
+ *
+ * Guesses the scope of a route object, based on the following rules:
+ * @code
+ *   1) Local route -> local scope
+ *   2) At least one nexthop not directly connected -> universe scope
+ *   3) All others -> link scope
+ * @endcode
+ *
+ * @return Scope value.
+ */
+int rtnl_route_guess_scope(struct rtnl_route *route)
+{
+	if (route->rt_type == RTN_LOCAL)
+		return RT_SCOPE_HOST;
+
+	if (!nl_list_empty(&route->rt_nexthops)) {
+		struct rtnl_nexthop *nh;
+
+		/*
+		 * Use scope uiniverse if there is at least one nexthop which
+		 * is not directly connected
+		 */
+		nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
+			if (nh->rtnh_gateway)
+				return RT_SCOPE_UNIVERSE;
+		}
+	}
+
+	return RT_SCOPE_LINK;
+}
+
+/** @} */
+
+static struct nla_policy route_policy[RTA_MAX+1] = {
+	[RTA_IIF]	= { .type = NLA_U32 },
+	[RTA_OIF]	= { .type = NLA_U32 },
+	[RTA_PRIORITY]	= { .type = NLA_U32 },
+	[RTA_FLOW]	= { .type = NLA_U32 },
+	[RTA_CACHEINFO]	= { .minlen = sizeof(struct rta_cacheinfo) },
+	[RTA_METRICS]	= { .type = NLA_NESTED },
+	[RTA_MULTIPATH]	= { .type = NLA_NESTED },
+};
+
+static int parse_multipath(struct rtnl_route *route, struct nlattr *attr)
+{
+	struct rtnl_nexthop *nh = NULL;
+	struct rtnexthop *rtnh = nla_data(attr);
+	size_t tlen = nla_len(attr);
+	int err;
+
+	while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) {
+		nh = rtnl_route_nh_alloc();
+		if (!nh)
+			return -NLE_NOMEM;
+
+		rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops);
+		rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex);
+		rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags);
+
+		if (rtnh->rtnh_len > sizeof(*rtnh)) {
+			struct nlattr *ntb[RTA_MAX + 1];
+
+			err = nla_parse(ntb, RTA_MAX, (struct nlattr *)
+					RTNH_DATA(rtnh),
+					rtnh->rtnh_len - sizeof(*rtnh),
+					route_policy);
+			if (err < 0)
+				goto errout;
+
+			if (ntb[RTA_GATEWAY]) {
+				struct nl_addr *addr;
+
+				addr = nl_addr_alloc_attr(ntb[RTA_GATEWAY],
+							  route->rt_family);
+				if (!addr) {
+					err = -NLE_NOMEM;
+					goto errout;
+				}
+
+				rtnl_route_nh_set_gateway(nh, addr);
+				nl_addr_put(addr);
+			}
+
+			if (ntb[RTA_FLOW]) {
+				uint32_t realms;
+				
+				realms = nla_get_u32(ntb[RTA_FLOW]);
+				rtnl_route_nh_set_realms(nh, realms);
+			}
+		}
+
+		rtnl_route_add_nexthop(route, nh);
+		tlen -= RTNH_ALIGN(rtnh->rtnh_len);
+		rtnh = RTNH_NEXT(rtnh);
+	}
+
+	err = 0;
+errout:
+	if (err && nh)
+		rtnl_route_nh_free(nh);
+
+	return err;
+}
+
+int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result)
+{
+	struct rtmsg *rtm;
+	struct rtnl_route *route;
+	struct nlattr *tb[RTA_MAX + 1];
+	struct nl_addr *src = NULL, *dst = NULL, *addr;
+	struct rtnl_nexthop *old_nh = NULL;
+	int err, family;
+
+	route = rtnl_route_alloc();
+	if (!route) {
+		err = -NLE_NOMEM;
+		goto errout;
+	}
+
+	route->ce_msgtype = nlh->nlmsg_type;
+
+	err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy);
+	if (err < 0)
+		goto errout;
+
+	rtm = nlmsg_data(nlh);
+	route->rt_family = family = rtm->rtm_family;
+	route->rt_tos = rtm->rtm_tos;
+	route->rt_table = rtm->rtm_table;
+	route->rt_type = rtm->rtm_type;
+	route->rt_scope = rtm->rtm_scope;
+	route->rt_protocol = rtm->rtm_protocol;
+	route->rt_flags = rtm->rtm_flags;
+
+	route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
+			  ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE |
+			  ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL |
+			  ROUTE_ATTR_FLAGS;
+
+	if (tb[RTA_DST]) {
+		if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family)))
+			goto errout_nomem;
+	} else {
+		if (!(dst = nl_addr_alloc(0)))
+			goto errout_nomem;
+		nl_addr_set_family(dst, rtm->rtm_family);
+	}
+
+	nl_addr_set_prefixlen(dst, rtm->rtm_dst_len);
+	err = rtnl_route_set_dst(route, dst);
+	if (err < 0)
+		goto errout;
+
+	nl_addr_put(dst);
+
+	if (tb[RTA_SRC]) {
+		if (!(src = nl_addr_alloc_attr(tb[RTA_SRC], family)))
+			goto errout_nomem;
+	} else if (rtm->rtm_src_len)
+		if (!(src = nl_addr_alloc(0)))
+			goto errout_nomem;
+
+	if (src) {
+		nl_addr_set_prefixlen(src, rtm->rtm_src_len);
+		rtnl_route_set_src(route, src);
+		nl_addr_put(src);
+	}
+
+	if (tb[RTA_IIF])
+		rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF]));
+
+	if (tb[RTA_PRIORITY])
+		rtnl_route_set_priority(route, nla_get_u32(tb[RTA_PRIORITY]));
+
+	if (tb[RTA_PREFSRC]) {
+		if (!(addr = nl_addr_alloc_attr(tb[RTA_PREFSRC], family)))
+			goto errout_nomem;
+		rtnl_route_set_pref_src(route, addr);
+		nl_addr_put(addr);
+	}
+
+	if (tb[RTA_METRICS]) {
+		struct nlattr *mtb[RTAX_MAX + 1];
+		int i;
+
+		err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL);
+		if (err < 0)
+			goto errout;
+
+		for (i = 1; i <= RTAX_MAX; i++) {
+			if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) {
+				uint32_t m = nla_get_u32(mtb[i]);
+				if (rtnl_route_set_metric(route, i, m) < 0)
+					goto errout;
+			}
+		}
+	}
+
+	if (tb[RTA_MULTIPATH])
+		if ((err = parse_multipath(route, tb[RTA_MULTIPATH])) < 0)
+			goto errout;
+
+	if (tb[RTA_CACHEINFO]) {
+		nla_memcpy(&route->rt_cacheinfo, tb[RTA_CACHEINFO],
+			   sizeof(route->rt_cacheinfo));
+		route->ce_mask |= ROUTE_ATTR_CACHEINFO;
+	}
+
+	if (tb[RTA_OIF]) {
+		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
+			goto errout;
+
+		rtnl_route_nh_set_ifindex(old_nh, nla_get_u32(tb[RTA_OIF]));
+	}
+
+	if (tb[RTA_GATEWAY]) {
+		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
+			goto errout;
+
+		if (!(addr = nl_addr_alloc_attr(tb[RTA_GATEWAY], family)))
+			goto errout_nomem;
+
+		rtnl_route_nh_set_gateway(old_nh, addr);
+		nl_addr_put(addr);
+	}
+
+	if (tb[RTA_FLOW]) {
+		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
+			goto errout;
+
+		rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW]));
+	}
+
+	if (old_nh) {
+		if (route->rt_nr_nh == 0) {
+			/* If no nexthops have been provided via RTA_MULTIPATH
+			 * we add it as regular nexthop to maintain backwards
+			 * compatibility */
+			rtnl_route_add_nexthop(route, old_nh);
+		} else {
+			/* Kernel supports new style nexthop configuration,
+			 * verify that it is a duplicate and discard nexthop. */
+			struct rtnl_nexthop *first;
+
+			first = nl_list_first_entry(&route->rt_nexthops,
+						    struct rtnl_nexthop,
+						    rtnh_list);
+			if (!first)
+				BUG();
+
+			if (rtnl_route_nh_compare(old_nh, first,
+						  old_nh->ce_mask, 0)) {
+				err = -NLE_INVAL;
+				goto errout;
+			}
+
+			rtnl_route_nh_free(old_nh);
+		}
+	}
+
+	*result = route;
+	return 0;
+
+errout:
+	rtnl_route_put(route);
+	return err;
+
+errout_nomem:
+	err = -NLE_NOMEM;
+	goto errout;
+}
+
+int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
+{
+	int i;
+	struct nlattr *metrics;
+	struct rtmsg rtmsg = {
+		.rtm_family = route->rt_family,
+		.rtm_tos = route->rt_tos,
+		.rtm_table = route->rt_table,
+		.rtm_protocol = route->rt_protocol,
+		.rtm_scope = route->rt_scope,
+		.rtm_type = route->rt_type,
+		.rtm_flags = route->rt_flags,
+	};
+
+	if (route->rt_dst == NULL)
+		return -NLE_MISSING_ATTR;
+
+	rtmsg.rtm_dst_len = nl_addr_get_prefixlen(route->rt_dst);
+	if (route->rt_src)
+		rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src);
+
+
+	if (rtmsg.rtm_scope == RT_SCOPE_NOWHERE)
+		rtmsg.rtm_scope = rtnl_route_guess_scope(route);
+
+	if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	/* Additional table attribute replacing the 8bit in the header, was
+	 * required to allow more than 256 tables. */
+	NLA_PUT_U32(msg, RTA_TABLE, route->rt_table);
+
+	if (nl_addr_get_len(route->rt_dst))
+		NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst);
+	NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio);
+
+	if (route->ce_mask & ROUTE_ATTR_SRC)
+		NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src);
+
+	if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
+		NLA_PUT_ADDR(msg, RTA_PREFSRC, route->rt_pref_src);
+
+	if (route->ce_mask & ROUTE_ATTR_IIF)
+		NLA_PUT_U32(msg, RTA_IIF, route->rt_iif);
+
+	if (route->rt_nmetrics > 0) {
+		uint32_t val;
+
+		metrics = nla_nest_start(msg, RTA_METRICS);
+		if (metrics == NULL)
+			goto nla_put_failure;
+
+		for (i = 1; i <= RTAX_MAX; i++) {
+			if (!rtnl_route_get_metric(route, i, &val))
+				NLA_PUT_U32(msg, i, val);
+		}
+
+		nla_nest_end(msg, metrics);
+	}
+
+	if (rtnl_route_get_nnexthops(route) > 0) {
+		struct nlattr *multipath;
+		struct rtnl_nexthop *nh;
+
+		if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH)))
+			goto nla_put_failure;
+
+		nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
+			struct rtnexthop *rtnh;
+
+			rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO);
+			if (!rtnh)
+				goto nla_put_failure;
+
+			rtnh->rtnh_flags = nh->rtnh_flags;
+			rtnh->rtnh_hops = nh->rtnh_weight;
+			rtnh->rtnh_ifindex = nh->rtnh_ifindex;
+
+			if (nh->rtnh_gateway)
+				NLA_PUT_ADDR(msg, RTA_GATEWAY,
+					     nh->rtnh_gateway);
+
+			if (nh->rtnh_realms)
+				NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
+
+			rtnh->rtnh_len = nlmsg_tail(msg->nm_nlh) -
+						(void *) rtnh;
+		}
+
+		nla_nest_end(msg, multipath);
+	}
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
+/** @cond SKIP */
 struct nl_object_ops route_obj_ops = {
 	.oo_name		= "route/route",
 	.oo_size		= sizeof(struct rtnl_route),
 	.oo_constructor		= route_constructor,
 	.oo_free_data		= route_free_data,
 	.oo_clone		= route_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= route_dump_brief,
-	.oo_dump[NL_DUMP_FULL]	= route_dump_full,
-	.oo_dump[NL_DUMP_STATS]	= route_dump_stats,
-	.oo_dump[NL_DUMP_XML]	= route_dump_xml,
-	.oo_dump[NL_DUMP_ENV]	= route_dump_env,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= route_dump_line,
+	    [NL_DUMP_DETAILS]	= route_dump_details,
+	    [NL_DUMP_STATS]	= route_dump_stats,
+	    [NL_DUMP_ENV]	= route_dump_env,
+	},
 	.oo_compare		= route_compare,
 	.oo_attrs2str		= route_attrs2str,
 	.oo_id_attrs		= (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
 				   ROUTE_ATTR_TABLE | ROUTE_ATTR_DST),
 };
+/** @endcond */
 
 /** @} */
diff --git a/lib/route/route_utils.c b/lib/route/route_utils.c
index a12d169..41ae65c 100644
--- a/lib/route/route_utils.c
+++ b/lib/route/route_utils.c
@@ -63,6 +63,11 @@
 	add_routing_table_name(RT_TABLE_LOCAL, "local");
 };
 
+static void __exit release_routing_table_names(void)
+{
+	__trans_list_clear(&table_names);
+}
+
 int rtnl_route_read_table_names(const char *path)
 {
 	__trans_list_clear(&table_names);
@@ -104,6 +109,11 @@
 	add_proto_name(RTPROT_STATIC, "static");
 };
 
+static void __exit release_proto_names(void)
+{
+	__trans_list_clear(&proto_names);
+}
+
 int rtnl_route_read_protocol_names(const char *path)
 {
 	__trans_list_clear(&proto_names);
@@ -157,27 +167,4 @@
 
 /** @} */
 
-/**
- * @name Nexthop Flags Translations
- * @{
- */
-
-static struct trans_tbl nh_flags[] = {
-	__ADD(RTNH_F_DEAD, dead)
-	__ADD(RTNH_F_PERVASIVE, pervasive)
-	__ADD(RTNH_F_ONLINK, onlink)
-};
-
-char * rtnl_route_nh_flags2str(int flags, char *buf, size_t len)
-{
-	return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags));
-}
-
-int rtnl_route_nh_str2flags(const char *name)
-{
-	return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags));
-}
-
-/** @} */
-
 /** @} */
diff --git a/lib/route/rtnl.c b/lib/route/rtnl.c
index 81ddf94..2533674 100644
--- a/lib/route/rtnl.c
+++ b/lib/route/rtnl.c
@@ -6,12 +6,11 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
- * @ingroup nlfam
- * @defgroup rtnl Routing Netlink
+ * @defgroup rtnl Routing Family
  * @{
  */
 
@@ -27,7 +26,7 @@
 
 /**
  * Send routing netlink request message
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg type		Netlink message type.
  * @arg family		Address family.
  * @arg flags		Additional netlink message flags.
@@ -37,13 +36,13 @@
  *
  * @return 0 on success or a negative error code.
  */
-int nl_rtgen_request(struct nl_handle *handle, int type, int family, int flags)
+int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags)
 {
 	struct rtgenmsg gmsg = {
 		.rtgen_family = family,
 	};
 
-	return nl_send_simple(handle, type, flags, &gmsg, sizeof(gmsg));
+	return nl_send_simple(sk, type, flags, &gmsg, sizeof(gmsg));
 }
 
 /** @} */
diff --git a/lib/route/rule.c b/lib/route/rule.c
index 60defd7..126e96d 100644
--- a/lib/route/rule.c
+++ b/lib/route/rule.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -60,15 +60,13 @@
 
 	if (src->r_src)
 		if (!(dst->r_src = nl_addr_clone(src->r_src)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	if (src->r_dst)
 		if (!(dst->r_dst = nl_addr_clone(src->r_dst)))
-			goto errout;
+			return -NLE_NOMEM;
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
 static struct nla_policy rule_policy[RTA_MAX+1] = {
@@ -85,11 +83,11 @@
 	struct rtnl_rule *rule;
 	struct rtmsg *r;
 	struct nlattr *tb[RTA_MAX+1];
-	int err = 1;
+	int err = 1, family;
 
 	rule = rtnl_rule_alloc();
 	if (!rule) {
-		err = nl_errno(ENOMEM);
+		err = -NLE_NOMEM;
 		goto errout;
 	}
 
@@ -100,14 +98,15 @@
 	if (err < 0)
 		goto errout;
 
-	rule->r_family = r->rtm_family;
+	rule->r_family = family = r->rtm_family;
 	rule->r_type = r->rtm_type;
 	rule->r_dsfield = r->rtm_tos;
 	rule->r_src_len = r->rtm_src_len;
 	rule->r_dst_len = r->rtm_dst_len;
 	rule->r_table = r->rtm_table;
 	rule->ce_mask = (RULE_ATTR_FAMILY | RULE_ATTR_TYPE | RULE_ATTR_DSFIELD |
-			 RULE_ATTR_SRC_LEN | RULE_ATTR_DST_LEN |RULE_ATTR_TYPE);
+			 RULE_ATTR_SRC_LEN | RULE_ATTR_DST_LEN |RULE_ATTR_TYPE |
+			 RULE_ATTR_TABLE);
 
 	if (tb[RTA_PRIORITY]) {
 		rule->r_prio = nla_get_u32(tb[RTA_PRIORITY]);
@@ -115,21 +114,15 @@
 	}
 
 	if (tb[RTA_SRC]) {
-		rule->r_src = nla_get_addr(tb[RTA_SRC], r->rtm_family);
-		if (!rule->r_src) {
-			err = nl_errno(ENOMEM);
-			goto errout;
-		}
+		if (!(rule->r_src = nl_addr_alloc_attr(tb[RTA_SRC], family)))
+			goto errout_enomem;
 		nl_addr_set_prefixlen(rule->r_src, r->rtm_src_len);
 		rule->ce_mask |= RULE_ATTR_SRC;
 	}
 
 	if (tb[RTA_DST]) {
-		rule->r_dst = nla_get_addr(tb[RTA_DST], r->rtm_family);
-		if (!rule->r_dst) {
-			err = nl_errno(ENOMEM);
-			goto errout;
-		}
+		if (!(rule->r_dst = nl_addr_alloc_attr(tb[RTA_DST], family)))
+			goto errout_enomem;
 		nl_addr_set_prefixlen(rule->r_dst, r->rtm_dst_len);
 		rule->ce_mask |= RULE_ATTR_DST;
 	}
@@ -150,216 +143,135 @@
 	}
 
 	if (tb[RTA_GATEWAY]) {
-		rule->r_srcmap = nla_get_addr(tb[RTA_GATEWAY], r->rtm_family);
-		if (!rule->r_srcmap) {
-			err = nl_errno(ENOMEM);
-			goto errout;
-		}
+		rule->r_srcmap = nl_addr_alloc_attr(tb[RTA_GATEWAY], family);
+		if (!rule->r_srcmap)
+			goto errout_enomem;
 		rule->ce_mask |= RULE_ATTR_SRCMAP;
 	}
 
+	if (tb[RTA_TABLE]) {
+            rule->r_table = nla_get_u32(tb[RTA_TABLE]);
+            rule->ce_mask |= RULE_ATTR_TABLE;
+        }
+
 	err = pp->pp_cb((struct nl_object *) rule, pp);
-	if (err < 0)
-		goto errout;
-
-	err = P_ACCEPT;
-
 errout:
 	rtnl_rule_put(rule);
 	return err;
+
+errout_enomem:
+	err = -NLE_NOMEM;
+	goto errout;
 }
 
-static int rule_request_update(struct nl_cache *c, struct nl_handle *h)
+static int rule_request_update(struct nl_cache *c, struct nl_sock *h)
 {
 	return nl_rtgen_request(h, RTM_GETRULE, AF_UNSPEC, NLM_F_DUMP);
 }
 
-static int rule_dump_brief(struct nl_object *o, struct nl_dump_params *p)
+static void rule_dump_line(struct nl_object *o, struct nl_dump_params *p)
 {
 	struct rtnl_rule *r = (struct rtnl_rule *) o;
 	char buf[128];
 
-	if (r->ce_mask & RULE_ATTR_PRIO)
-		dp_dump(p, "%d:\t", r->r_prio);
-	else
-		dp_dump(p, "0:\t");
+	nl_dump_line(p, "%8d ", (r->ce_mask & RULE_ATTR_PRIO) ? r->r_prio : 0);
+	nl_dump(p, "%s ", nl_af2str(r->r_family, buf, sizeof(buf)));
 
 	if (r->ce_mask & RULE_ATTR_SRC)
-		dp_dump(p, "from %s ",
+		nl_dump(p, "from %s ",
 			nl_addr2str(r->r_src, buf, sizeof(buf)));
 	else if (r->ce_mask & RULE_ATTR_SRC_LEN && r->r_src_len)
-		dp_dump(p, "from 0/%d ", r->r_src_len);
+		nl_dump(p, "from 0/%d ", r->r_src_len);
 
 	if (r->ce_mask & RULE_ATTR_DST)
-		dp_dump(p, "to %s ",
+		nl_dump(p, "to %s ",
 			nl_addr2str(r->r_dst, buf, sizeof(buf)));
 	else if (r->ce_mask & RULE_ATTR_DST_LEN && r->r_dst_len)
-		dp_dump(p, "to 0/%d ", r->r_dst_len);
+		nl_dump(p, "to 0/%d ", r->r_dst_len);
 
 	if (r->ce_mask & RULE_ATTR_DSFIELD && r->r_dsfield)
-		dp_dump(p, "tos %d ", r->r_dsfield);
+		nl_dump(p, "tos %d ", r->r_dsfield);
 
 	if (r->ce_mask & RULE_ATTR_MARK)
-		dp_dump(p, "mark %" PRIx64 , r->r_mark);
+		nl_dump(p, "mark %" PRIx64 , r->r_mark);
 
 	if (r->ce_mask & RULE_ATTR_IIF)
-		dp_dump(p, "iif %s ", r->r_iif);
+		nl_dump(p, "iif %s ", r->r_iif);
 
 	if (r->ce_mask & RULE_ATTR_TABLE)
-		dp_dump(p, "lookup %s ",
+		nl_dump(p, "lookup %s ",
 			rtnl_route_table2str(r->r_table, buf, sizeof(buf)));
 
 	if (r->ce_mask & RULE_ATTR_REALMS)
-		dp_dump(p, "realms %s ",
+		nl_dump(p, "realms %s ",
 			rtnl_realms2str(r->r_realms, buf, sizeof(buf)));
 
-	dp_dump(p, "action %s\n",
+	nl_dump(p, "action %s\n",
 		nl_rtntype2str(r->r_type, buf, sizeof(buf)));
-
-	return 1;
 }
 
-static int rule_dump_full(struct nl_object *obj, struct nl_dump_params *p)
+static void rule_dump_details(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_rule *rule = (struct rtnl_rule *) obj;
 	char buf[128];
-	int line;
 
-	line = rule_dump_brief(obj, p);
-
-	dp_dump_line(p, line++, "  family %s",
-		     nl_af2str(rule->r_family, buf, sizeof(buf)));
+	rule_dump_line(obj, p);
 
 	if (rule->ce_mask & RULE_ATTR_SRCMAP)
-		dp_dump(p, " srcmap %s",
+		nl_dump_line(p, "  srcmap %s\n",
 			nl_addr2str(rule->r_srcmap, buf, sizeof(buf)));
-
-	dp_dump(p, "\n");
-
-	return line;
 }
 
-static int rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+static void rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
 {
-	return rule_dump_full(obj, p);
+	rule_dump_details(obj, p);
 }
 
-static int rule_dump_xml(struct nl_object *obj, struct nl_dump_params *p)
+static void rule_dump_env(struct nl_object *obj, struct nl_dump_params *p)
 {
 	struct rtnl_rule *rule = (struct rtnl_rule *) obj;
 	char buf[128];
-	int line = 0;
-	
-	dp_dump_line(p, line++, "<rule>\n");
 
-	dp_dump_line(p, line++, "  <priority>%u</priority>\n",
-		     rule->r_prio);
-	dp_dump_line(p, line++, "  <family>%s</family>\n",
+	nl_dump_line(p, "RULE_PRIORITY=%u\n", rule->r_prio);
+	nl_dump_line(p, "RULE_FAMILY=%s\n",
 		     nl_af2str(rule->r_family, buf, sizeof(buf)));
 
 	if (rule->ce_mask & RULE_ATTR_DST)
-		dp_dump_line(p, line++, "  <dst>%s</dst>\n",
+		nl_dump_line(p, "RULE_DST=%s\n",
 			     nl_addr2str(rule->r_dst, buf, sizeof(buf)));
 
 	if (rule->ce_mask & RULE_ATTR_DST_LEN)
-		dp_dump_line(p, line++, "  <dstlen>%u</dstlen>\n",
-			     rule->r_dst_len);
+		nl_dump_line(p, "RULE_DSTLEN=%u\n", rule->r_dst_len);
 
 	if (rule->ce_mask & RULE_ATTR_SRC)
-		dp_dump_line(p, line++, "  <src>%s</src>\n",
+		nl_dump_line(p, "RULE_SRC=%s\n",
 			     nl_addr2str(rule->r_src, buf, sizeof(buf)));
 
 	if (rule->ce_mask & RULE_ATTR_SRC_LEN)
-		dp_dump_line(p, line++, "  <srclen>%u</srclen>\n",
-			     rule->r_src_len);
+		nl_dump_line(p, "RULE_SRCLEN=%u\n", rule->r_src_len);
 
 	if (rule->ce_mask & RULE_ATTR_IIF)
-		dp_dump_line(p, line++, "  <iif>%s</iif>\n", rule->r_iif);
+		nl_dump_line(p, "RULE_IIF=%s\n", rule->r_iif);
 
 	if (rule->ce_mask & RULE_ATTR_TABLE)
-		dp_dump_line(p, line++, "  <table>%u</table>\n",
-			     rule->r_table);
+		nl_dump_line(p, "RULE_TABLE=%u\n", rule->r_table);
 
 	if (rule->ce_mask & RULE_ATTR_REALMS)
-		dp_dump_line(p, line++, "  <realms>%u</realms>\n",
-			     rule->r_realms);
+		nl_dump_line(p, "RULE_REALM=%u\n", rule->r_realms);
 
 	if (rule->ce_mask & RULE_ATTR_MARK)
-		dp_dump_line(p, line++, "  <mark>%" PRIx64 "</mark>\n",
-			     rule->r_mark);
+		nl_dump_line(p, "RULE_MARK=0x%" PRIx64 "\n", rule->r_mark);
 
 	if (rule->ce_mask & RULE_ATTR_DSFIELD)
-		dp_dump_line(p, line++, "  <dsfield>%u</dsfield>\n",
-			     rule->r_dsfield);
+		nl_dump_line(p, "RULE_DSFIELD=%u\n", rule->r_dsfield);
 
 	if (rule->ce_mask & RULE_ATTR_TYPE)
-		dp_dump_line(p, line++, "<type>%s</type>\n",
+		nl_dump_line(p, "RULE_TYPE=%s\n",
 			     nl_rtntype2str(rule->r_type, buf, sizeof(buf)));
 
 	if (rule->ce_mask & RULE_ATTR_SRCMAP)
-		dp_dump_line(p, line++, "<srcmap>%s</srcmap>\n",
+		nl_dump_line(p, "RULE_SRCMAP=%s\n",
 			     nl_addr2str(rule->r_srcmap, buf, sizeof(buf)));
-
-	dp_dump_line(p, line++, "</rule>\n");
-
-	return line;
-}
-
-static int rule_dump_env(struct nl_object *obj, struct nl_dump_params *p)
-{
-	struct rtnl_rule *rule = (struct rtnl_rule *) obj;
-	char buf[128];
-	int line = 0;
-
-	dp_dump_line(p, line++, "RULE_PRIORITY=%u\n",
-		     rule->r_prio);
-	dp_dump_line(p, line++, "RULE_FAMILY=%s\n",
-		     nl_af2str(rule->r_family, buf, sizeof(buf)));
-
-	if (rule->ce_mask & RULE_ATTR_DST)
-		dp_dump_line(p, line++, "RULE_DST=%s\n",
-			     nl_addr2str(rule->r_dst, buf, sizeof(buf)));
-
-	if (rule->ce_mask & RULE_ATTR_DST_LEN)
-		dp_dump_line(p, line++, "RULE_DSTLEN=%u\n",
-			     rule->r_dst_len);
-
-	if (rule->ce_mask & RULE_ATTR_SRC)
-		dp_dump_line(p, line++, "RULE_SRC=%s\n",
-			     nl_addr2str(rule->r_src, buf, sizeof(buf)));
-
-	if (rule->ce_mask & RULE_ATTR_SRC_LEN)
-		dp_dump_line(p, line++, "RULE_SRCLEN=%u\n",
-			     rule->r_src_len);
-
-	if (rule->ce_mask & RULE_ATTR_IIF)
-		dp_dump_line(p, line++, "RULE_IIF=%s\n", rule->r_iif);
-
-	if (rule->ce_mask & RULE_ATTR_TABLE)
-		dp_dump_line(p, line++, "RULE_TABLE=%u\n",
-			     rule->r_table);
-
-	if (rule->ce_mask & RULE_ATTR_REALMS)
-		dp_dump_line(p, line++, "RULE_REALM=%u\n",
-			     rule->r_realms);
-
-	if (rule->ce_mask & RULE_ATTR_MARK)
-		dp_dump_line(p, line++, "RULE_MARK=0x%" PRIx64 "\n",
-			     rule->r_mark);
-
-	if (rule->ce_mask & RULE_ATTR_DSFIELD)
-		dp_dump_line(p, line++, "RULE_DSFIELD=%u\n",
-			     rule->r_dsfield);
-
-	if (rule->ce_mask & RULE_ATTR_TYPE)
-		dp_dump_line(p, line++, "RULE_TYPE=%s\n",
-			     nl_rtntype2str(rule->r_type, buf, sizeof(buf)));
-
-	if (rule->ce_mask & RULE_ATTR_SRCMAP)
-		dp_dump_line(p, line++, "RULE_SRCMAP=%s\n",
-			     nl_addr2str(rule->r_srcmap, buf, sizeof(buf)));
-
-	return line;
 }
 
 static int rule_compare(struct nl_object *_a, struct nl_object *_b,
@@ -434,51 +346,34 @@
  */
 
 /**
- * Build a rule cache including all rules of the specified family currently configured in the kernel.
- * @arg handle		netlink handle
- * @arg family		address family
- *
- * Allocates a new rule cache, initializes it properly and updates it
- * to include all rules of the specified address family currently
- * configured in the kernel.
- *
- * @note The caller is responsible for destroying and freeing the
- *       cache after using it. (nl_cache_destroy_and_free())
- * @return The new cache or NULL if an error occured.
- */
-struct nl_cache * rtnl_rule_alloc_cache_by_family(struct nl_handle *handle,
-						  int family)
-{
-	struct nl_cache * cache;
-
-	cache = nl_cache_alloc(&rtnl_rule_ops);
-	if (cache == NULL)
-		return NULL;
-
-	/* XXX RULE_CACHE_FAMILY(cache) = family; */
-
-	if (handle && nl_cache_refill(handle, cache) < 0) {
-		free(cache);
-		return NULL;
-	}
-
-	return cache;
-}
-
-/**
  * Build a rule cache including all rules currently configured in the kernel.
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
+ * @arg family		Address family or AF_UNSPEC.
+ * @arg result		Pointer to store resulting cache.
  *
  * Allocates a new rule cache, initializes it properly and updates it
  * to include all rules currently configured in the kernel.
  *
- * @note The caller is responsible for destroying and freeing the
- *       cache after using it. (nl_cache_destroy_and_free())
- * @return The new cache or NULL if an error occured.
+ * @return 0 on success or a negative error code.
  */
-struct nl_cache * rtnl_rule_alloc_cache(struct nl_handle *handle)
+int rtnl_rule_alloc_cache(struct nl_sock *sock, int family,
+			  struct nl_cache **result)
 {
-	return rtnl_rule_alloc_cache_by_family(handle, AF_UNSPEC);
+	struct nl_cache * cache;
+	int err;
+
+	if (!(cache = nl_cache_alloc(&rtnl_rule_ops)))
+		return -NLE_NOMEM;
+
+	cache->c_iarg1 = family;
+
+	if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
+		free(cache);
+		return err;
+	}
+
+	*result = cache;
+	return 0;
 }
 
 /** @} */
@@ -488,7 +383,8 @@
  * @{
  */
 
-static struct nl_msg *build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags)
+static int build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags,
+			  struct nl_msg **result)
 {
 	struct nl_msg *msg;
 	struct rtmsg rtm = {
@@ -518,7 +414,7 @@
 
 	msg = nlmsg_alloc_simple(cmd, flags);
 	if (!msg)
-		goto nla_put_failure;
+		return -NLE_NOMEM;
 
 	if (nlmsg_append(msg, &rtm, sizeof(rtm), NLMSG_ALIGNTO) < 0)
 		goto nla_put_failure;
@@ -541,11 +437,12 @@
 	if (tmpl->ce_mask & RULE_ATTR_IIF)
 		NLA_PUT_STRING(msg, RTA_IIF, tmpl->r_iif);
 
-	return msg;
+	*result = msg;
+	return 0;
 
 nla_put_failure:
 	nlmsg_free(msg);
-	return NULL;
+	return -NLE_MSGSIZE;
 }
 
 /**
@@ -561,14 +458,16 @@
  * 
  * @return The netlink message
  */
-struct nl_msg *rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags)
+int rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags,
+				struct nl_msg **result)
 {
-	return build_rule_msg(tmpl, RTM_NEWRULE, NLM_F_CREATE | flags);
+	return build_rule_msg(tmpl, RTM_NEWRULE, NLM_F_CREATE | flags,
+			      result);
 }
 
 /**
  * Add a new rule
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg tmpl		template with requested changes
  * @arg flags		additional netlink message flags
  *
@@ -578,21 +477,20 @@
  *
  * @return 0 on sucess or a negative error if an error occured.
  */
-int rtnl_rule_add(struct nl_handle *handle, struct rtnl_rule *tmpl, int flags)
+int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags)
 {
-	int err;
 	struct nl_msg *msg;
+	int err;
 	
-	msg = rtnl_rule_build_add_request(tmpl, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_rule_build_add_request(tmpl, flags, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -615,14 +513,15 @@
  *
  * @return The netlink message
  */
-struct nl_msg *rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags)
+int rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags,
+				   struct nl_msg **result)
 {
-	return build_rule_msg(rule, RTM_DELRULE, flags);
+	return build_rule_msg(rule, RTM_DELRULE, flags, result);
 }
 
 /**
  * Delete a rule
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg rule		rule to delete
  * @arg flags		additional netlink message flags
  *
@@ -632,22 +531,20 @@
  *
  * @return 0 on sucess or a negative error if an error occured.
  */
-int rtnl_rule_delete(struct nl_handle *handle, struct rtnl_rule *rule,
-		     int flags)
+int rtnl_rule_delete(struct nl_sock *sk, struct rtnl_rule *rule, int flags)
 {
-	int err;
 	struct nl_msg *msg;
+	int err;
 	
-	msg = rtnl_rule_build_delete_request(rule, flags);
-	if (!msg)
-		return nl_errno(ENOMEM);
+	if ((err = rtnl_rule_build_delete_request(rule, flags, &msg)) < 0)
+		return err;
 
-	err = nl_send_auto_complete(handle, msg);
+	err = nl_send_auto_complete(sk, msg);
+	nlmsg_free(msg);
 	if (err < 0)
 		return err;
 
-	nlmsg_free(msg);
-	return nl_wait_for_ack(handle);
+	return wait_for_ack(sk);
 }
 
 /** @} */
@@ -764,7 +661,7 @@
 {
 	if (rule->ce_mask & RULE_ATTR_FAMILY) {
 		if (new->a_family != rule->r_family)
-			return nl_error(EINVAL, "Address family mismatch");
+			return -NLE_AF_MISMATCH;
 	} else
 		rule->r_family = new->a_family;
 
@@ -811,7 +708,7 @@
 int rtnl_rule_set_iif(struct rtnl_rule *rule, const char *dev)
 {
 	if (strlen(dev) > IFNAMSIZ-1)
-		return nl_errno(ERANGE);
+		return -NLE_RANGE;
 
 	strcpy(rule->r_iif, dev);
 	rule->ce_mask |= RULE_ATTR_IIF;
@@ -837,16 +734,16 @@
 	if (rule->ce_mask & RULE_ATTR_TYPE)
 		return rule->r_type;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
-void rtnl_rule_set_realms(struct rtnl_rule *rule, realm_t realms)
+void rtnl_rule_set_realms(struct rtnl_rule *rule, uint32_t realms)
 {
 	rule->r_realms = realms;
 	rule->ce_mask |= RULE_ATTR_REALMS;
 }
 
-realm_t rtnl_rule_get_realms(struct rtnl_rule *rule)
+uint32_t rtnl_rule_get_realms(struct rtnl_rule *rule)
 {
 	if (rule->ce_mask & RULE_ATTR_REALMS)
 		return rule->r_realms;
@@ -861,11 +758,12 @@
 	.oo_size		= sizeof(struct rtnl_rule),
 	.oo_free_data		= rule_free_data,
 	.oo_clone		= rule_clone,
-	.oo_dump[NL_DUMP_BRIEF]	= rule_dump_brief,
-	.oo_dump[NL_DUMP_FULL]	= rule_dump_full,
-	.oo_dump[NL_DUMP_STATS]	= rule_dump_stats,
-	.oo_dump[NL_DUMP_XML]	= rule_dump_xml,
-	.oo_dump[NL_DUMP_ENV]	= rule_dump_env,
+	.oo_dump = {
+	    [NL_DUMP_LINE]	= rule_dump_line,
+	    [NL_DUMP_DETAILS]	= rule_dump_details,
+	    [NL_DUMP_STATS]	= rule_dump_stats,
+	    [NL_DUMP_ENV]	= rule_dump_env,
+	},
 	.oo_compare		= rule_compare,
 	.oo_attrs2str		= rule_attrs2str,
 	.oo_id_attrs		= ~0,
diff --git a/lib/route/sch/cbq.c b/lib/route/sch/cbq.c
index 9808509..1aaa58d 100644
--- a/lib/route/sch/cbq.c
+++ b/lib/route/sch/cbq.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 #include <netlink-local.h>
@@ -99,7 +99,7 @@
 
 	cbq = cbq_alloc(tca);
 	if (!cbq)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	nla_memcpy(&cbq->cbq_lss, tb[TCA_CBQ_LSSOPT], sizeof(cbq->cbq_lss));
 	nla_memcpy(&cbq->cbq_rate, tb[TCA_CBQ_RATE], sizeof(cbq->cbq_rate));
@@ -133,7 +133,7 @@
 	struct rtnl_cbq *src = cbq_qdisc(_src);
 
 	if (src && !cbq_alloc(_dst))
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 	else
 		return 0;
 }
@@ -153,8 +153,7 @@
 	return cbq_clone((struct rtnl_tca *) dst, (struct rtnl_tca *) src);
 }
 
-static int cbq_dump_brief(struct rtnl_tca *tca, struct nl_dump_params *p,
-			  int line)
+static void cbq_dump_line(struct rtnl_tca *tca, struct nl_dump_params *p)
 {
 	struct rtnl_cbq *cbq;
 	double r, rbit;
@@ -162,32 +161,28 @@
 
 	cbq = cbq_qdisc(tca);
 	if (!cbq)
-		goto ignore;
+		return;
 
 	r = nl_cancel_down_bytes(cbq->cbq_rate.rate, &ru);
 	rbit = nl_cancel_down_bits(cbq->cbq_rate.rate * 8, &rubit);
 
-	dp_dump(p, " rate %.2f%s/s (%.0f%s) prio %u",
+	nl_dump(p, " rate %.2f%s/s (%.0f%s) prio %u",
 		r, ru, rbit, rubit, cbq->cbq_wrr.priority);
-
-ignore:
-	return line;
 }
 
-static int cbq_qdisc_dump_brief(struct rtnl_qdisc *qdisc,
-				struct nl_dump_params *p, int line)
+static void cbq_qdisc_dump_line(struct rtnl_qdisc *qdisc,
+				struct nl_dump_params *p)
 {
-	return cbq_dump_brief((struct rtnl_tca *) qdisc, p, line);
+	cbq_dump_line((struct rtnl_tca *) qdisc, p);
 }
 
-static int cbq_class_dump_brief(struct rtnl_class *class,
-				struct nl_dump_params *p, int line)
+static void cbq_class_dump_line(struct rtnl_class *class,
+				struct nl_dump_params *p)
 {
-	return cbq_dump_brief((struct rtnl_tca *) class, p, line);
+	cbq_dump_line((struct rtnl_tca *) class, p);
 }
 
-static int cbq_dump_full(struct rtnl_tca *tca, struct nl_dump_params *p,
-			 int line)
+static void cbq_dump_details(struct rtnl_tca *tca, struct nl_dump_params *p)
 {
 	struct rtnl_cbq *cbq;
 	char *unit, buf[32];
@@ -196,18 +191,18 @@
 
 	cbq = cbq_qdisc(tca);
 	if (!cbq)
-		goto ignore;
+		return;
 
 	w = nl_cancel_down_bits(cbq->cbq_wrr.weight * 8, &unit);
 
-	dp_dump(p, "avgpkt %u mpu %u cell %u allot %u weight %.0f%s\n",
+	nl_dump(p, "avgpkt %u mpu %u cell %u allot %u weight %.0f%s\n",
 		cbq->cbq_lss.avpkt,
 		cbq->cbq_rate.mpu,
 		1 << cbq->cbq_rate.cell_log,
 		cbq->cbq_wrr.allot, w, unit);
 
 	el = cbq->cbq_lss.ewma_log;
-	dp_dump_line(p, line++, "  minidle %uus maxidle %uus offtime "
+	nl_dump_line(p, "  minidle %uus maxidle %uus offtime "
 				"%uus level %u ewma_log %u\n",
 		nl_ticks2us(cbq->cbq_lss.minidle >> el),
 		nl_ticks2us(cbq->cbq_lss.maxidle >> el),
@@ -215,60 +210,53 @@
 		cbq->cbq_lss.level,
 		cbq->cbq_lss.ewma_log);
 
-	dp_dump_line(p, line++, "  penalty %uus strategy %s ",
+	nl_dump_line(p, "  penalty %uus strategy %s ",
 		nl_ticks2us(cbq->cbq_ovl.penalty),
 		nl_ovl_strategy2str(cbq->cbq_ovl.strategy, buf, sizeof(buf)));
 
-	dp_dump(p, "split %s defmap 0x%08x ",
+	nl_dump(p, "split %s defmap 0x%08x ",
 		rtnl_tc_handle2str(cbq->cbq_fopt.split, buf, sizeof(buf)),
 		cbq->cbq_fopt.defmap);
 	
-	dp_dump(p, "police %s",
+	nl_dump(p, "police %s",
 		nl_police2str(cbq->cbq_police.police, buf, sizeof(buf)));
-
-ignore:
-	return line;
 }
 
-static int cbq_qdisc_dump_full(struct rtnl_qdisc *qdisc,
-			       struct nl_dump_params *p, int line)
+static void cbq_qdisc_dump_details(struct rtnl_qdisc *qdisc,
+				   struct nl_dump_params *p)
 {
-	return cbq_dump_full((struct rtnl_tca *) qdisc, p, line);
+	cbq_dump_details((struct rtnl_tca *) qdisc, p);
 }
 
-static int cbq_class_dump_full(struct rtnl_class *class,
-			       struct nl_dump_params *p, int line)
+static void cbq_class_dump_details(struct rtnl_class *class,
+				   struct nl_dump_params *p)
 {
-	return cbq_dump_full((struct rtnl_tca *) class, p, line);
+	cbq_dump_details((struct rtnl_tca *) class, p);
 }
 
-static int cbq_dump_with_stats(struct rtnl_tca *tca, struct nl_dump_params *p,
-			       int line)
+static void cbq_dump_stats(struct rtnl_tca *tca, struct nl_dump_params *p)
 {
 	struct tc_cbq_xstats *x = tca_xstats(tca);
 
 	if (!x)
-		goto ignore;
+		return;
 
-	dp_dump_line(p, line++, "            borrows    overact  "
-				"  avgidle  undertime\n");
-	dp_dump_line(p, line++, "         %10u %10u %10u %10u\n",
+	nl_dump_line(p, "            borrows    overact  "
+			"  avgidle  undertime\n");
+	nl_dump_line(p, "         %10u %10u %10u %10u\n",
 		     x->borrows, x->overactions, x->avgidle, x->undertime);
-
-ignore:
-	return line;
 }
 
-static int cbq_qdisc_dump_with_stats(struct rtnl_qdisc *qdisc,
-				     struct nl_dump_params *p, int line)
+static void cbq_qdisc_dump_stats(struct rtnl_qdisc *qdisc,
+				 struct nl_dump_params *p)
 {
-	return cbq_dump_with_stats((struct rtnl_tca *) qdisc, p, line);
+	cbq_dump_stats((struct rtnl_tca *) qdisc, p);
 }
 
-static int cbq_class_dump_with_stats(struct rtnl_class *class,
-				     struct nl_dump_params *p, int line)
+static void cbq_class_dump_stats(struct rtnl_class *class,
+				 struct nl_dump_params *p)
 {
-	return cbq_dump_with_stats((struct rtnl_tca *) class, p, line);
+	cbq_dump_stats((struct rtnl_tca *) class, p);
 }
 
 static struct rtnl_qdisc_ops cbq_qdisc_ops = {
@@ -276,9 +264,11 @@
 	.qo_msg_parser		= cbq_qdisc_msg_parser,
 	.qo_free_data		= cbq_qdisc_free_data,
 	.qo_clone		= cbq_qdisc_clone,
-	.qo_dump[NL_DUMP_BRIEF]	= cbq_qdisc_dump_brief,
-	.qo_dump[NL_DUMP_FULL]	= cbq_qdisc_dump_full,
-	.qo_dump[NL_DUMP_STATS]	= cbq_qdisc_dump_with_stats,
+	.qo_dump = {
+	    [NL_DUMP_LINE]	= cbq_qdisc_dump_line,
+	    [NL_DUMP_DETAILS]	= cbq_qdisc_dump_details,
+	    [NL_DUMP_STATS]	= cbq_qdisc_dump_stats,
+	},
 };
 
 static struct rtnl_class_ops cbq_class_ops = {
@@ -286,9 +276,11 @@
 	.co_msg_parser		= cbq_class_msg_parser,
 	.co_free_data		= cbq_class_free_data,
 	.co_clone		= cbq_class_clone,
-	.co_dump[NL_DUMP_BRIEF]	= cbq_class_dump_brief,
-	.co_dump[NL_DUMP_FULL]	= cbq_class_dump_full,
-	.co_dump[NL_DUMP_STATS]	= cbq_class_dump_with_stats,
+	.co_dump = {
+	    [NL_DUMP_LINE]	= cbq_class_dump_line,
+	    [NL_DUMP_DETAILS]	= cbq_class_dump_details,
+	    [NL_DUMP_STATS]	= cbq_class_dump_stats,
+	},
 };
 
 static void __init cbq_init(void)
diff --git a/lib/route/sch/dsmark.c b/lib/route/sch/dsmark.c
index 5ba6b92..61b0fea 100644
--- a/lib/route/sch/dsmark.c
+++ b/lib/route/sch/dsmark.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -70,7 +70,7 @@
 
 	dsmark = dsmark_qdisc_alloc(qdisc);
 	if (!dsmark)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	if (tb[TCA_DSMARK_INDICES]) {
 		dsmark->qdm_indices = nla_get_u16(tb[TCA_DSMARK_INDICES]);
@@ -118,7 +118,7 @@
 
 	dsmark = dsmark_class_alloc(class);
 	if (!dsmark)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	if (tb[TCA_DSMARK_MASK]) {
 		dsmark->cdm_bmask = nla_get_u8(tb[TCA_DSMARK_MASK]);
@@ -133,51 +133,43 @@
 	return 0;
 }
 
-static int dsmark_qdisc_dump_brief(struct rtnl_qdisc *qdisc,
-				   struct nl_dump_params *p, int line)
+static void dsmark_qdisc_dump_line(struct rtnl_qdisc *qdisc,
+				   struct nl_dump_params *p)
 {
 	struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc);
 
 	if (dsmark && (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES))
-		dp_dump(p, " indices 0x%04x", dsmark->qdm_indices);
-
-	return line;
+		nl_dump(p, " indices 0x%04x", dsmark->qdm_indices);
 }
 
-static int dsmark_qdisc_dump_full(struct rtnl_qdisc *qdisc,
-				  struct nl_dump_params *p, int line)
+static void dsmark_qdisc_dump_details(struct rtnl_qdisc *qdisc,
+				      struct nl_dump_params *p)
 {
 	struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc);
 
 	if (!dsmark)
-		goto ignore;
+		return;
 
 	if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX)
-		dp_dump(p, " default index 0x%04x", dsmark->qdm_default_index);
+		nl_dump(p, " default index 0x%04x", dsmark->qdm_default_index);
 
 	if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX)
-		dp_dump(p, " set-tc-index");
-
-ignore:
-	return line;
+		nl_dump(p, " set-tc-index");
 }
 
-static int dsmark_class_dump_brief(struct rtnl_class *class,
-				   struct nl_dump_params *p, int line)
+static void dsmark_class_dump_line(struct rtnl_class *class,
+				   struct nl_dump_params *p)
 {
 	struct rtnl_dsmark_class *dsmark = dsmark_class(class);
 
 	if (!dsmark)
-		goto ignore;
+		return;
 
 	if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE)
-		dp_dump(p, " value 0x%02x", dsmark->cdm_value);
+		nl_dump(p, " value 0x%02x", dsmark->cdm_value);
 
 	if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK)
-		dp_dump(p, " mask 0x%02x", dsmark->cdm_bmask);
-
-ignore:
-	return line;
+		nl_dump(p, " mask 0x%02x", dsmark->cdm_bmask);
 }
 
 static struct nl_msg *dsmark_qdisc_get_opts(struct rtnl_qdisc *qdisc)
@@ -251,7 +243,7 @@
 	
 	dsmark = dsmark_class(class);
 	if (!dsmark)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	dsmark->cdm_bmask = mask;
 	dsmark->cdm_mask |= SCH_DSMARK_ATTR_MASK;
@@ -272,7 +264,7 @@
 	if (dsmark && dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK)
 		return dsmark->cdm_bmask;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -287,7 +279,7 @@
 
 	dsmark = dsmark_class(class);
 	if (!dsmark)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	dsmark->cdm_value = value;
 	dsmark->cdm_mask |= SCH_DSMARK_ATTR_VALUE;
@@ -308,7 +300,7 @@
 	if (dsmark && dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE)
 		return dsmark->cdm_value;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /** @} */
@@ -329,7 +321,7 @@
 
 	dsmark = dsmark_qdisc(qdisc);
 	if (!dsmark)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	dsmark->qdm_indices = indices;
 	dsmark->qdm_mask |= SCH_DSMARK_ATTR_INDICES;
@@ -350,7 +342,7 @@
 	if (dsmark && dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES)
 		return dsmark->qdm_indices;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -366,7 +358,7 @@
 
 	dsmark = dsmark_qdisc(qdisc);
 	if (!dsmark)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	dsmark->qdm_default_index = default_index;
 	dsmark->qdm_mask |= SCH_DSMARK_ATTR_DEFAULT_INDEX;
@@ -387,7 +379,7 @@
 	if (dsmark && dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX)
 		return dsmark->qdm_default_index;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -402,7 +394,7 @@
 
 	dsmark = dsmark_qdisc(qdisc);
 	if (!dsmark)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	dsmark->qdm_set_tc_index = !!flag;
 	dsmark->qdm_mask |= SCH_DSMARK_ATTR_SET_TC_INDEX;
@@ -424,7 +416,7 @@
 	if (dsmark && dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX)
 		return dsmark->qdm_set_tc_index;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /** @} */
@@ -432,15 +424,17 @@
 static struct rtnl_qdisc_ops dsmark_qdisc_ops = {
 	.qo_kind		= "dsmark",
 	.qo_msg_parser		= dsmark_qdisc_msg_parser,
-	.qo_dump[NL_DUMP_BRIEF]	= dsmark_qdisc_dump_brief,
-	.qo_dump[NL_DUMP_FULL]	= dsmark_qdisc_dump_full,
+	.qo_dump = {
+	    [NL_DUMP_LINE]	= dsmark_qdisc_dump_line,
+	    [NL_DUMP_DETAILS]	= dsmark_qdisc_dump_details,
+	},
 	.qo_get_opts		= dsmark_qdisc_get_opts,
 };
 
 static struct rtnl_class_ops dsmark_class_ops = {
 	.co_kind		= "dsmark",
 	.co_msg_parser		= dsmark_class_msg_parser,
-	.co_dump[NL_DUMP_BRIEF]	= dsmark_class_dump_brief,
+	.co_dump[NL_DUMP_LINE]	= dsmark_class_dump_line,
 	.co_get_opts		= dsmark_class_get_opts,
 };
 
diff --git a/lib/route/sch/fifo.c b/lib/route/sch/fifo.c
index 4f8d202..464af30 100644
--- a/lib/route/sch/fifo.c
+++ b/lib/route/sch/fifo.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -60,11 +60,11 @@
 	struct tc_fifo_qopt *opt;
 
 	if (qdisc->q_opts->d_size < sizeof(struct tc_fifo_qopt))
-		return nl_error(EINVAL, "FIFO options size mismatch");
+		return -NLE_INVAL;
 
 	fifo = fifo_alloc(qdisc);
 	if (!fifo)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	opt = (struct tc_fifo_qopt *) qdisc->q_opts->d_data;
 	fifo->qf_limit = opt->limit;
@@ -78,19 +78,15 @@
 	free(qdisc->q_subdata);
 }
 
-static int pfifo_dump_brief(struct rtnl_qdisc *qdisc,
-			    struct nl_dump_params *p, int line)
+static void pfifo_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_fifo *fifo = fifo_qdisc(qdisc);
 
 	if (fifo)
-		dp_dump(p, " limit %u packets", fifo->qf_limit);
-
-	return line;
+		nl_dump(p, " limit %u packets", fifo->qf_limit);
 }
 
-static int bfifo_dump_brief(struct rtnl_qdisc *qdisc,
-			    struct nl_dump_params *p, int line)
+static void bfifo_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_fifo *fifo = fifo_qdisc(qdisc);
 
@@ -99,10 +95,8 @@
 		double r;
 
 		r = nl_cancel_down_bytes(fifo->qf_limit, &unit);
-		dp_dump(p, " limit %.1f%s", r, unit);
+		nl_dump(p, " limit %.1f%s", r, unit);
 	}
-
-	return line;
 }
 
 static struct nl_msg *fifo_get_opts(struct rtnl_qdisc *qdisc)
@@ -148,7 +142,7 @@
 	
 	fifo = fifo_alloc(qdisc);
 	if (!fifo)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 		
 	fifo->qf_limit = limit;
 	fifo->qf_mask |= SCH_FIFO_ATTR_LIMIT;
@@ -169,7 +163,7 @@
 	if (fifo && fifo->qf_mask & SCH_FIFO_ATTR_LIMIT)
 		return fifo->qf_limit;
 	else
-		return nl_errno(ENOMEM);
+		return -NLE_NOATTR;
 }
 
 /** @} */
@@ -178,7 +172,7 @@
 	.qo_kind		= "pfifo",
 	.qo_msg_parser		= fifo_msg_parser,
 	.qo_free_data		= fifo_free_data,
-	.qo_dump[NL_DUMP_BRIEF]	= pfifo_dump_brief,
+	.qo_dump[NL_DUMP_LINE]	= pfifo_dump_line,
 	.qo_get_opts		= fifo_get_opts,
 };
 
@@ -186,7 +180,7 @@
 	.qo_kind		= "bfifo",
 	.qo_msg_parser		= fifo_msg_parser,
 	.qo_free_data		= fifo_free_data,
-	.qo_dump[NL_DUMP_BRIEF]	= bfifo_dump_brief,
+	.qo_dump[NL_DUMP_LINE]	= bfifo_dump_line,
 	.qo_get_opts		= fifo_get_opts,
 };
 
diff --git a/lib/route/sch/htb.c b/lib/route/sch/htb.c
index 6de87b3..a167136 100644
--- a/lib/route/sch/htb.c
+++ b/lib/route/sch/htb.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
  * Copyright (c) 2005-2006 Siemens AG Oesterreich
  */
@@ -136,34 +136,31 @@
 	free(class->c_subdata);
 }
 
-static int htb_qdisc_dump_brief(struct rtnl_qdisc *qdisc,
-				struct nl_dump_params *p, int line)
+static void htb_qdisc_dump_line(struct rtnl_qdisc *qdisc,
+				struct nl_dump_params *p)
 {
 	struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
 
 	if (d == NULL)
-		goto ignore;
+		return;
 
 	if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
-		dp_dump(p, " r2q %u", d->qh_rate2quantum);
+		nl_dump(p, " r2q %u", d->qh_rate2quantum);
 
 	if (d->qh_mask & SCH_HTB_HAS_DEFCLS) {
 		char buf[32];
-		dp_dump(p, " default %s",
+		nl_dump(p, " default %s",
 			rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf)));
 	}
-
-ignore:
-	return line;
 }
 
-static int htb_class_dump_brief(struct rtnl_class *class,
-				struct nl_dump_params *p, int line)
+static void htb_class_dump_line(struct rtnl_class *class,
+				struct nl_dump_params *p)
 {
 	struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
 
 	if (d == NULL)
-		goto ignore;
+		return;
 
 	if (d->ch_mask & SCH_HTB_HAS_RATE) {
 		double r, rbit;
@@ -172,21 +169,18 @@
 		r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru);
 		rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit);
 
-		dp_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
+		nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
 			r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log);
 	}
-
-ignore:
-	return line;
 }
 
-static int htb_class_dump_full(struct rtnl_class *class,
-			       struct nl_dump_params *p, int line)
+static void htb_class_dump_details(struct rtnl_class *class,
+				   struct nl_dump_params *p)
 {
 	struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
 
 	if (d == NULL)
-		goto ignore;
+		return;
 
 	/* line 1 */
 	if (d->ch_mask & SCH_HTB_HAS_CEIL) {
@@ -196,22 +190,22 @@
 		r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru);
 		rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit);
 
-		dp_dump(p, "    ceil %.2f%s/s (%.0f%s) log %u",
+		nl_dump(p, "    ceil %.2f%s/s (%.0f%s) log %u",
 			r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log);
 	}
 
 	if (d->ch_mask & SCH_HTB_HAS_PRIO)
-		dp_dump(p, " prio %u", d->ch_prio);
+		nl_dump(p, " prio %u", d->ch_prio);
 
 	if (d->ch_mask & SCH_HTB_HAS_MTU)
-		dp_dump(p, " mtu %u", d->ch_mtu);
+		nl_dump(p, " mtu %u", d->ch_mtu);
 
 	if (d->ch_mask & SCH_HTB_HAS_RBUFFER) {
 		double b;
 		char *bu;
 
 		b = nl_cancel_down_bytes(d->ch_rbuffer, &bu);
-		dp_dump(p, " rbuffer %.2f%s", b, bu);
+		nl_dump(p, " rbuffer %.2f%s", b, bu);
 	}
 
 	if (d->ch_mask & SCH_HTB_HAS_CBUFFER) {
@@ -219,20 +213,17 @@
 		char *bu;
 
 		b = nl_cancel_down_bytes(d->ch_cbuffer, &bu);
-		dp_dump(p, " cbuffer %.2f%s", b, bu);
+		nl_dump(p, " cbuffer %.2f%s", b, bu);
 	}
 
 	if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
-		dp_dump(p, " quantum %u", d->ch_quantum);
+		nl_dump(p, " quantum %u", d->ch_quantum);
 
 	if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
-		dp_dump(p, " overhead %u", d->ch_overhead);
+		nl_dump(p, " overhead %u", d->ch_overhead);
 
 	if (d->ch_mask & SCH_HTB_HAS_MPU)
-		dp_dump(p, " mpu %u", d->ch_mpu);
-
-ignore:
-	return line;
+		nl_dump(p, " mpu %u", d->ch_mpu);
 }
 
 static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc)
@@ -525,7 +516,7 @@
 	.qo_kind		= "htb",
 	.qo_msg_parser		= htb_qdisc_msg_parser,
 	.qo_free_data		= htb_qdisc_free_data,
-	.qo_dump[NL_DUMP_BRIEF]	= htb_qdisc_dump_brief,
+	.qo_dump[NL_DUMP_LINE]	= htb_qdisc_dump_line,
 	.qo_get_opts		= htb_qdisc_get_opts,
 };
 
@@ -533,8 +524,10 @@
 	.co_kind		= "htb",
 	.co_msg_parser		= htb_class_msg_parser,
 	.co_free_data		= htb_class_free_data,
-	.co_dump[NL_DUMP_BRIEF]	= htb_class_dump_brief,
-	.co_dump[NL_DUMP_FULL]	= htb_class_dump_full,
+	.co_dump = {
+	    [NL_DUMP_LINE]	= htb_class_dump_line,
+	    [NL_DUMP_DETAILS]	= htb_class_dump_details,
+	},
 	.co_get_opts		= htb_class_get_opts,
 };
 
diff --git a/lib/route/sch/netem.c b/lib/route/sch/netem.c
index e8b8913..18878a7 100644
--- a/lib/route/sch/netem.c
+++ b/lib/route/sch/netem.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -27,17 +27,20 @@
 #include <netlink/route/sch/netem.h>
 
 /** @cond SKIP */
-#define SCH_NETEM_ATTR_LATENCY		0x001
-#define SCH_NETEM_ATTR_LIMIT		0x002
-#define SCH_NETEM_ATTR_LOSS		0x004
-#define SCH_NETEM_ATTR_GAP		0x008
-#define SCH_NETEM_ATTR_DUPLICATE	0x010
-#define SCH_NETEM_ATTR_JITTER		0x020
-#define SCH_NETEM_ATTR_DELAY_CORR	0x040
-#define SCH_NETEM_ATTR_LOSS_CORR	0x080
-#define SCH_NETEM_ATTR_DUP_CORR		0x100
-#define SCH_NETEM_ATTR_RO_PROB		0x200
-#define SCH_NETEM_ATTR_RO_CORR		0x400
+#define SCH_NETEM_ATTR_LATENCY		0x0001
+#define SCH_NETEM_ATTR_LIMIT		0x0002
+#define SCH_NETEM_ATTR_LOSS			0x0004
+#define SCH_NETEM_ATTR_GAP			0x0008
+#define SCH_NETEM_ATTR_DUPLICATE	0x0010
+#define SCH_NETEM_ATTR_JITTER		0x0020
+#define SCH_NETEM_ATTR_DELAY_CORR	0x0040
+#define SCH_NETEM_ATTR_LOSS_CORR	0x0080
+#define SCH_NETEM_ATTR_DUP_CORR		0x0100
+#define SCH_NETEM_ATTR_RO_PROB		0x0200
+#define SCH_NETEM_ATTR_RO_CORR		0x0400
+#define SCH_NETEM_ATTR_CORRUPT_PROB	0x0800
+#define SCH_NETEM_ATTR_CORRUPT_CORR	0x1000
+#define SCH_NETEM_ATTR_DIST         0x2000
 /** @endcond */
 
 static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc)
@@ -56,6 +59,7 @@
 static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
 	[TCA_NETEM_CORR]	= { .minlen = sizeof(struct tc_netem_corr) },
 	[TCA_NETEM_REORDER]	= { .minlen = sizeof(struct tc_netem_reorder) },
+	[TCA_NETEM_CORRUPT]	= { .minlen = sizeof(struct tc_netem_corrupt) },
 };
 
 static int netem_msg_parser(struct rtnl_qdisc *qdisc)
@@ -65,11 +69,11 @@
 	struct tc_netem_qopt *opts;
 
 	if (qdisc->q_opts->d_size < sizeof(*opts))
-		return nl_error(EINVAL, "Netem specific options size mismatch");
+		return -NLE_INVAL;
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data;
 	netem->qnm_latency = opts->latency;
@@ -89,7 +93,7 @@
 		struct nlattr *tb[TCA_NETEM_MAX+1];
 
 		err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
-				qdisc->q_opts->d_data + sizeof(*opts),
+				(qdisc->q_opts->d_data + sizeof(*opts)),
 				len, netem_policy);
 		if (err < 0) {
 			free(netem);
@@ -106,7 +110,7 @@
 
 			netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
 					    SCH_NETEM_ATTR_LOSS_CORR |
-					    SCH_NETEM_ATTR_DELAY_CORR);
+					SCH_NETEM_ATTR_DUP_CORR);
 		}
 
 		if (tb[TCA_NETEM_REORDER]) {
@@ -119,6 +123,21 @@
 			netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
 					    SCH_NETEM_ATTR_RO_CORR);
 		}
+			
+		if (tb[TCA_NETEM_CORRUPT]) {
+			struct tc_netem_corrupt corrupt;
+						
+			nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt));
+			netem->qnm_crpt.nmcr_probability = corrupt.probability;
+			netem->qnm_crpt.nmcr_correlation = corrupt.correlation;
+	
+			netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
+						SCH_NETEM_ATTR_CORRUPT_CORR);
+		}
+		
+		/* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */
+		netem->qnm_dist.dist_data = NULL;
+		netem->qnm_dist.dist_size = 0;
 	}
 
 	return 0;
@@ -126,29 +145,164 @@
 
 static void netem_free_data(struct rtnl_qdisc *qdisc)
 {
-	free(qdisc->q_subdata);
+	struct rtnl_netem *netem;
+	
+	if ( ! qdisc ) return;
+	
+	netem = netem_qdisc(qdisc);
+	if ( ! netem ) return;
+	
+	if ( netem->qnm_dist.dist_data )
+		free(netem->qnm_dist.dist_data);
+	
+	netem = NULL;
+	
+	free (qdisc->q_subdata);
 }
 
-static int netem_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
-			    int line)
+static void netem_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_netem *netem = netem_qdisc(qdisc);
 
 	if (netem)
-		dp_dump(p, "limit %d", netem->qnm_limit);
-
-	return line;
+		nl_dump(p, "limit %d", netem->qnm_limit);
 }
 
-static int netem_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
-			   int line)
+int netem_build_msg(struct rtnl_qdisc *qdisc, struct nl_msg *msg)
 {
-	return line;
-}
+	int err = 0;
+	struct tc_netem_qopt opts;
+	struct tc_netem_corr cor;
+	struct tc_netem_reorder reorder;
+	struct tc_netem_corrupt corrupt;
+	struct rtnl_netem *netem;
+	
+	unsigned char set_correlation = 0, set_reorder = 0,
+		set_corrupt = 0, set_dist = 0;
 
-static struct nl_msg *netem_get_opts(struct rtnl_qdisc *qdisc)
-{
-	return NULL;
+	memset(&opts, 0, sizeof(opts));
+	memset(&cor, 0, sizeof(cor));
+	memset(&reorder, 0, sizeof(reorder));
+	memset(&corrupt, 0, sizeof(corrupt));
+
+	netem = netem_qdisc(qdisc);
+	if (!netem || !msg)
+		return EFAULT;
+		
+	msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
+	
+	if ( netem->qnm_ro.nmro_probability != 0 ) {
+		if (netem->qnm_latency == 0) {
+			return -NLE_MISSING_ATTR;
+		}
+		if (netem->qnm_gap == 0) netem->qnm_gap = 1;
+	}
+	else if ( netem->qnm_gap ) { 
+		return -NLE_MISSING_ATTR;
+	}
+
+	if ( netem->qnm_corr.nmc_delay != 0 ) {
+		if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
+			return -NLE_MISSING_ATTR;
+		}
+		set_correlation = 1;
+	}
+	
+	if ( netem->qnm_corr.nmc_loss != 0 ) {
+		if ( netem->qnm_loss == 0 ) {
+			return -NLE_MISSING_ATTR;
+		}
+		set_correlation = 1;
+	}
+
+	if ( netem->qnm_corr.nmc_duplicate != 0 ) {
+		if ( netem->qnm_duplicate == 0 ) {
+			return -NLE_MISSING_ATTR;
+		}
+		set_correlation = 1;
+	}
+	
+	if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1;
+	else if ( netem->qnm_ro.nmro_correlation != 0 ) {
+			return -NLE_MISSING_ATTR;
+	}
+	
+	if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1;
+	else if ( netem->qnm_crpt.nmcr_correlation != 0 ) {
+			return -NLE_MISSING_ATTR;
+	}
+	
+	if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) {
+		if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
+			return -NLE_MISSING_ATTR;
+	}
+	else {
+		/* Resize to accomodate the large distribution table */
+		int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
+			sizeof(netem->qnm_dist.dist_data[0]);
+		
+		msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len);
+		if ( msg->nm_nlh == NULL )
+			return -NLE_NOMEM;
+		msg->nm_size = new_msg_len;
+			set_dist = 1;
+		}
+	}
+	
+	opts.latency = netem->qnm_latency;
+	opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000;
+	opts.loss = netem->qnm_loss;
+	opts.gap = netem->qnm_gap;
+	opts.duplicate = netem->qnm_duplicate;
+	opts.jitter = netem->qnm_jitter;
+	
+	NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts);
+	
+	if ( set_correlation ) {
+		cor.delay_corr = netem->qnm_corr.nmc_delay;
+		cor.loss_corr = netem->qnm_corr.nmc_loss;
+		cor.dup_corr = netem->qnm_corr.nmc_duplicate;
+
+		NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor);
+	}
+	
+	if ( set_reorder ) {
+		reorder.probability = netem->qnm_ro.nmro_probability;
+		reorder.correlation = netem->qnm_ro.nmro_correlation;
+
+		NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
+	}
+	
+	if ( set_corrupt ) {
+		corrupt.probability = netem->qnm_crpt.nmcr_probability;
+		corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
+
+		NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
+	}
+	
+	if ( set_dist ) {
+		NLA_PUT(msg, TCA_NETEM_DELAY_DIST,
+			netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]),
+			netem->qnm_dist.dist_data);
+	}
+
+	/* Length specified in the TCA_OPTIONS section must span the entire
+	 * remainder of the message. That's just the way that sch_netem expects it.
+	 * Maybe there's a more succinct way to do this at a higher level.
+	 */
+	struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) +
+		NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
+		
+	struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) +
+		NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
+	
+	int old_len = head->nla_len;
+	head->nla_len = (void *)tail - (void *)head;
+	msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
+	
+	return err;
+nla_put_failure:
+	return -NLE_MSGSIZE;
 }
 
 /**
@@ -168,7 +322,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 	
 	netem->qnm_limit = limit;
 	netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
@@ -189,7 +343,7 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT))
 		return netem->qnm_limit;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /** @} */
@@ -211,7 +365,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_gap = gap;
 	netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
@@ -232,7 +386,7 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP))
 		return netem->qnm_gap;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -247,7 +401,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_ro.nmro_probability = prob;
 	netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
@@ -268,7 +422,7 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB))
 		return netem->qnm_ro.nmro_probability;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -283,7 +437,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_ro.nmro_correlation = prob;
 	netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
@@ -304,7 +458,86 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR))
 		return netem->qnm_ro.nmro_correlation;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
+}
+
+/** @} */
+
+/**
+ * @name Corruption
+ * @{
+ */
+ 
+/**
+ * Set corruption probability of netem qdisc.
+ * @arg qdisc		Netem qdisc to be modified.
+ * @arg prob		New corruption probability.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
+{
+	struct rtnl_netem *netem;
+
+	netem = netem_alloc(qdisc);
+	if (!netem)
+		return -NLE_NOMEM;
+
+	netem->qnm_crpt.nmcr_probability = prob;
+	netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB;
+
+	return 0;
+}
+
+/**
+ * Get corruption probability of netem qdisc.
+ * @arg qdisc		Netem qdisc.
+ * @return Corruption probability or a negative error code.
+ */
+int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_netem *netem;
+
+	netem = netem_qdisc(qdisc);
+	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB))
+		return netem->qnm_crpt.nmcr_probability;
+	else
+		return -NLE_NOATTR;
+}
+
+/**
+ * Set corruption correlation probability of netem qdisc.
+ * @arg qdisc		Netem qdisc to be modified.
+ * @arg prob		New corruption correlation probability.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
+{
+	struct rtnl_netem *netem;
+
+	netem = netem_alloc(qdisc);
+	if (!netem)
+		return -NLE_NOMEM;
+
+	netem->qnm_crpt.nmcr_correlation = prob;
+	netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR;
+
+	return 0;
+}
+
+/**
+ * Get corruption correlation probability of netem qdisc.
+ * @arg qdisc		Netem qdisc.
+ * @return Corruption correlation probability or a negative error code.
+ */
+int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_netem *netem;
+
+	netem = netem_qdisc(qdisc);
+	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR))
+		return netem->qnm_crpt.nmcr_correlation;
+	else
+		return -NLE_NOATTR;
 }
 
 /** @} */
@@ -326,7 +559,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_loss = prob;
 	netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
@@ -347,7 +580,7 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS))
 		return netem->qnm_loss;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -362,7 +595,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_corr.nmc_loss = prob;
 	netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
@@ -383,7 +616,7 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR))
 		return netem->qnm_corr.nmc_loss;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /** @} */
@@ -405,7 +638,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_duplicate = prob;
 	netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
@@ -426,7 +659,7 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE))
 		return netem->qnm_duplicate;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -441,7 +674,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_corr.nmc_duplicate = prob;
 	netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
@@ -462,7 +695,7 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR))
 		return netem->qnm_corr.nmc_duplicate;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /** @} */
@@ -484,7 +717,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_latency = nl_us2ticks(delay);
 	netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
@@ -505,7 +738,7 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY))
 		return nl_ticks2us(netem->qnm_latency);
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -520,7 +753,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_jitter = nl_us2ticks(jitter);
 	netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
@@ -541,7 +774,7 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER))
 		return nl_ticks2us(netem->qnm_jitter);
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -555,7 +788,7 @@
 
 	netem = netem_alloc(qdisc);
 	if (!netem)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	netem->qnm_corr.nmc_delay = prob;
 	netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
@@ -576,7 +809,110 @@
 	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR))
 		return netem->qnm_corr.nmc_delay;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
+}
+
+/**
+ * Get the size of the distribution table.
+ * @arg qdisc		Netem qdisc.
+ * @return Distribution table size or a negative error code.
+ */
+int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
+{
+	struct rtnl_netem *netem;
+
+	netem = netem_qdisc(qdisc);
+	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST))
+		return netem->qnm_dist.dist_size;
+	else
+		return -NLE_NOATTR;
+}
+
+/**
+ * Get a pointer to the distribution table.
+ * @arg qdisc		Netem qdisc.
+ * @arg dist_ptr	The pointer to set.
+ * @return Negative error code on failure or 0 on success.
+ */
+int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
+{
+	struct rtnl_netem *netem;
+
+	netem = netem_qdisc(qdisc);
+	if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) {
+		*dist_ptr = netem->qnm_dist.dist_data;
+		return 0;
+	}
+	else
+		return -NLE_NOATTR;
+}
+
+/**
+ * Set the delay distribution. Latency/jitter must be set before applying.
+ * @arg qdisc Netem qdisc.
+ * @arg dist_type The name of the distribution (type, file, path/file).
+ * @return 0 on success, error code on failure.
+ */
+int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
+	struct rtnl_netem *netem;
+
+	netem = netem_alloc(qdisc);
+	if (!netem)
+		return -NLE_NOMEM;
+		
+	FILE *f = NULL;
+	int i, n = 0;
+	size_t len = 2048;
+	char *line;
+	char name[NAME_MAX];
+	char dist_suffix[] = ".dist";
+	
+	/* If the given filename already ends in .dist, don't append it later */
+	char *test_suffix = strstr(dist_type, dist_suffix);
+	if (test_suffix != NULL && strlen(test_suffix) == 5)
+		strcpy(dist_suffix, "");
+	
+	/* Check several locations for the dist file */
+	char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" };
+	
+	for (i = 0; i < sizeof(test_path) && f == NULL; i++) {
+		snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix);
+		f = fopen(name, "r");
+	}
+	
+	if ( f == NULL )
+		return -nl_syserr2nlerr(errno);
+	
+	netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t));
+	
+	line = (char *) calloc (sizeof(char), len + 1);
+	
+	while (getline(&line, &len, f) != -1) {
+		char *p, *endp;
+		
+		if (*line == '\n' || *line == '#')
+			continue;
+
+		for (p = line; ; p = endp) {
+			long x = strtol(p, &endp, 0);
+			if (endp == p) break;
+
+			if (n >= MAXDIST) {
+				free(line);
+				fclose(f);
+				return -NLE_INVAL;
+			}
+			netem->qnm_dist.dist_data[n++] = x;
+		}		
+	}
+	
+	free(line);
+	
+	netem->qnm_dist.dist_size = n;
+	netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
+	
+	fclose(f);
+	return 0;	
 }
 
 /** @} */
@@ -585,9 +921,9 @@
 	.qo_kind		= "netem",
 	.qo_msg_parser		= netem_msg_parser,
 	.qo_free_data		= netem_free_data,
-	.qo_dump[NL_DUMP_BRIEF]	= netem_dump_brief,
-	.qo_dump[NL_DUMP_FULL]	= netem_dump_full,
-	.qo_get_opts		= netem_get_opts,
+	.qo_dump[NL_DUMP_LINE]	= netem_dump_line,
+	.qo_get_opts		= 0,
+	.qo_build_msg	= netem_build_msg
 };
 
 static void __init netem_init(void)
diff --git a/lib/route/sch/prio.c b/lib/route/sch/prio.c
index 4e3d624..4c9ebcf 100644
--- a/lib/route/sch/prio.c
+++ b/lib/route/sch/prio.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -58,11 +58,11 @@
 	struct tc_prio_qopt *opt;
 
 	if (qdisc->q_opts->d_size < sizeof(*opt))
-		return nl_error(EINVAL, "prio specific option size mismatch");
+		return -NLE_INVAL;
 
 	prio = prio_alloc(qdisc);
 	if (!prio)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	opt = (struct tc_prio_qopt *) qdisc->q_opts->d_data;
 	prio->qp_bands = opt->bands;
@@ -77,55 +77,48 @@
 	free(qdisc->q_subdata);
 }
 
-static int prio_dump_brief(struct rtnl_qdisc *qdisc,
-			   struct nl_dump_params *p, int line)
+static void prio_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_prio *prio = prio_qdisc(qdisc);
 
 	if (prio)
-		dp_dump(p, " bands %u", prio->qp_bands);
-
-	return line;
+		nl_dump(p, " bands %u", prio->qp_bands);
 }
 
-static int prio_dump_full(struct rtnl_qdisc *qdisc,
-			  struct nl_dump_params *p, int line)
+static void prio_dump_details(struct rtnl_qdisc *qdisc,struct nl_dump_params *p)
 {
 	struct rtnl_prio *prio = prio_qdisc(qdisc);
 	int i, hp;
 
 	if (!prio)
-		goto ignore;
+		return;
 
-	dp_dump(p, "priomap [");
+	nl_dump(p, "priomap [");
 	
 	for (i = 0; i <= TC_PRIO_MAX; i++)
-		dp_dump(p, "%u%s", prio->qp_priomap[i],
+		nl_dump(p, "%u%s", prio->qp_priomap[i],
 			i < TC_PRIO_MAX ? " " : "");
 
-	dp_dump(p, "]\n");
-	dp_new_line(p, line++);
+	nl_dump(p, "]\n");
+	nl_new_line(p);
 
 	hp = (((TC_PRIO_MAX/2) + 1) & ~1);
 
 	for (i = 0; i < hp; i++) {
 		char a[32];
-		dp_dump(p, "    %18s => %u",
+		nl_dump(p, "    %18s => %u",
 			rtnl_prio2str(i, a, sizeof(a)),
 			prio->qp_priomap[i]);
 		if (hp+i <= TC_PRIO_MAX) {
-			dp_dump(p, " %18s => %u",
+			nl_dump(p, " %18s => %u",
 				rtnl_prio2str(hp+i, a, sizeof(a)),
 				prio->qp_priomap[hp+i]);
 			if (i < (hp - 1)) {
-				dp_dump(p, "\n");
-				dp_new_line(p, line++);
+				nl_dump(p, "\n");
+				nl_new_line(p);
 			}
 		}
 	}
-
-ignore:
-	return line;
 }
 
 static struct nl_msg *prio_get_opts(struct rtnl_qdisc *qdisc)
@@ -173,7 +166,7 @@
 	
 	prio = prio_alloc(qdisc);
 	if (!prio)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	prio->qp_bands = bands;
 	prio->qp_mask |= SCH_PRIO_ATTR_BANDS;
@@ -194,7 +187,7 @@
 	if (prio && prio->qp_mask & SCH_PRIO_ATTR_BANDS)
 		return prio->qp_bands;
 	else
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 }
 
 /**
@@ -212,18 +205,17 @@
 
 	prio = prio_alloc(qdisc);
 	if (!prio)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS))
-		return nl_error(EINVAL, "Set number of bands first");
+		return -NLE_MISSING_ATTR;
 
 	if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1))
-		return nl_error(ERANGE, "priomap length out of bounds");
+		return -NLE_RANGE;
 
 	for (i = 0; i <= TC_PRIO_MAX; i++) {
 		if (priomap[i] > prio->qp_bands)
-			return nl_error(ERANGE, "priomap element %d " \
-			    "out of bounds, increase bands number");
+			return -NLE_RANGE;
 	}
 
 	memcpy(prio->qp_priomap, priomap, len);
@@ -245,10 +237,8 @@
 	prio = prio_qdisc(qdisc);
 	if (prio && prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)
 		return prio->qp_priomap;
-	else {
-		nl_errno(ENOENT);
+	else
 		return NULL;
-	}
 }
 
 /** @} */
@@ -303,8 +293,10 @@
 	.qo_kind		= "prio",
 	.qo_msg_parser		= prio_msg_parser,
 	.qo_free_data		= prio_free_data,
-	.qo_dump[NL_DUMP_BRIEF]	= prio_dump_brief,
-	.qo_dump[NL_DUMP_FULL]	= prio_dump_full,
+	.qo_dump = {
+	    [NL_DUMP_LINE]	= prio_dump_line,
+	    [NL_DUMP_DETAILS]	= prio_dump_details,
+	},
 	.qo_get_opts		= prio_get_opts,
 };
 
@@ -312,8 +304,10 @@
 	.qo_kind		= "pfifo_fast",
 	.qo_msg_parser		= prio_msg_parser,
 	.qo_free_data		= prio_free_data,
-	.qo_dump[NL_DUMP_BRIEF]	= prio_dump_brief,
-	.qo_dump[NL_DUMP_FULL]	= prio_dump_full,
+	.qo_dump = {
+	    [NL_DUMP_LINE]	= prio_dump_line,
+	    [NL_DUMP_DETAILS]	= prio_dump_details,
+	},
 	.qo_get_opts		= prio_get_opts,
 };
 
diff --git a/lib/route/sch/red.c b/lib/route/sch/red.c
index a31c358..e4cac79 100644
--- a/lib/route/sch/red.c
+++ b/lib/route/sch/red.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -66,11 +66,11 @@
 		return err;
 
 	if (!tb[TCA_RED_PARMS])
-		return nl_error(EINVAL, "Missing TCA_RED_PARMS");
+		return -NLE_MISSING_ATTR;
 
 	red = red_alloc(qdisc);
 	if (!red)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	opts = nla_data(tb[TCA_RED_PARMS]);
 
@@ -89,40 +89,31 @@
 	return 0;
 }
 
-static int red_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
-			  int line)
+static void red_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_red *red = red_qdisc(qdisc);
 
 	if (red) {
 		/* XXX: limit, min, max, flags */
 	}
-
-	return line;
 }
 
-static int red_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
-			 int line)
+static void red_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_red *red = red_qdisc(qdisc);
 
 	if (red) {
 		/* XXX: wlog, plog, scell_log */
 	}
-
-	return line;
 }
 
-static int red_dump_stats(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
-			  int line)
+static void red_dump_stats(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_red *red = red_qdisc(qdisc);
 
 	if (red) {
 		/* XXX: xstats */
 	}
-
-	return line;
 }
 
 static struct nl_msg *red_get_opts(struct rtnl_qdisc *qdisc)
@@ -171,7 +162,7 @@
 
 	red = red_alloc(qdisc);
 	if (!red)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	red->qr_limit = limit;
 	red->qr_mask |= RED_ATTR_LIMIT;
@@ -192,7 +183,7 @@
 	if (red && (red->qr_mask & RED_ATTR_LIMIT))
 		return red->qr_limit;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /** @} */
@@ -200,9 +191,11 @@
 static struct rtnl_qdisc_ops red_ops = {
 	.qo_kind		= "red",
 	.qo_msg_parser		= red_msg_parser,
-	.qo_dump[NL_DUMP_BRIEF]	= red_dump_brief,
-	.qo_dump[NL_DUMP_FULL]	= red_dump_full,
-	.qo_dump[NL_DUMP_STATS]	= red_dump_stats,
+	.qo_dump = {
+	    [NL_DUMP_LINE]	= red_dump_line,
+	    [NL_DUMP_DETAILS]	= red_dump_details,
+	    [NL_DUMP_STATS]	= red_dump_stats,
+	},
 	.qo_get_opts		= red_get_opts,
 };
 
diff --git a/lib/route/sch/sfq.c b/lib/route/sch/sfq.c
index d530c0f..4b47356 100644
--- a/lib/route/sch/sfq.c
+++ b/lib/route/sch/sfq.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -61,11 +61,11 @@
 		return 0;
 
 	if (qdisc->q_opts->d_size < sizeof(*opts))
-		return nl_error(EINVAL, "SFQ specific options size mismatch");
+		return -NLE_INVAL;
 
 	sfq = sfq_alloc(qdisc);
 	if (!sfq)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	opts = (struct tc_sfq_qopt *) qdisc->q_opts->d_data;
 
@@ -87,29 +87,22 @@
 	free(qdisc->q_subdata);
 }
 
-static int sfq_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
-			  int line)
+static void sfq_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_sfq *sfq = sfq_qdisc(qdisc);
 
 	if (sfq)
-		dp_dump(p, " quantum %u perturb %us",
-			sfq->qs_quantum,
+		nl_dump(p, " quantum %u perturb %us", sfq->qs_quantum,
 			nl_ticks2us(sfq->qs_perturb * nl_get_hz()));
-
-	return line;
 }
 
-static int sfq_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
-			 int line)
+static void sfq_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_sfq *sfq = sfq_qdisc(qdisc);
 
 	if (sfq)
-		dp_dump(p, "limit %u divisor %u",
+		nl_dump(p, "limit %u divisor %u",
 			sfq->qs_limit, sfq->qs_divisor);
-
-	return line;
 }
 
 static struct nl_msg *sfq_get_opts(struct rtnl_qdisc *qdisc)
@@ -157,7 +150,7 @@
 	
 	sfq = sfq_alloc(qdisc);
 	if (!sfq)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	sfq->qs_quantum = quantum;
 	sfq->qs_mask |= SCH_SFQ_ATTR_QUANTUM;
@@ -178,7 +171,7 @@
 	if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_QUANTUM)
 		return sfq->qs_quantum;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -193,7 +186,7 @@
 
 	sfq = sfq_alloc(qdisc);
 	if (!sfq)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	sfq->qs_limit = limit;
 	sfq->qs_mask |= SCH_SFQ_ATTR_LIMIT;
@@ -214,7 +207,7 @@
 	if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_LIMIT)
 		return sfq->qs_limit;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -230,7 +223,7 @@
 
 	sfq = sfq_alloc(qdisc);
 	if (!sfq)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	sfq->qs_perturb = perturb;
 	sfq->qs_mask |= SCH_SFQ_ATTR_PERTURB;
@@ -251,7 +244,7 @@
 	if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_PERTURB)
 		return sfq->qs_perturb;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /**
@@ -267,7 +260,7 @@
 	if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_DIVISOR)
 		return sfq->qs_divisor;
 	else
-		return nl_errno(ENOENT);
+		return -NLE_NOATTR;
 }
 
 /** @} */
@@ -276,8 +269,10 @@
 	.qo_kind		= "sfq",
 	.qo_msg_parser		= sfq_msg_parser,
 	.qo_free_data		= sfq_free_data,
-	.qo_dump[NL_DUMP_BRIEF]	= sfq_dump_brief,
-	.qo_dump[NL_DUMP_FULL]	= sfq_dump_full,
+	.qo_dump = {
+	    [NL_DUMP_LINE]	= sfq_dump_line,
+	    [NL_DUMP_DETAILS]	= sfq_dump_details,
+	},
 	.qo_get_opts		= sfq_get_opts,
 };
 
diff --git a/lib/route/sch/tbf.c b/lib/route/sch/tbf.c
index 04d1689..eccaf70 100644
--- a/lib/route/sch/tbf.c
+++ b/lib/route/sch/tbf.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -62,9 +62,9 @@
 	if (err < 0)
 		return err;
 	
-	tbf = tbf_qdisc(q);
+	tbf = tbf_alloc(q);
 	if (!tbf)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	if (tb[TCA_TBF_PARMS]) {
 		struct tc_tbf_qopt opts;
@@ -93,34 +93,34 @@
 	return 0;
 }
 
-static int tbf_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
-			  int line)
+static void tbf_free_data(struct rtnl_qdisc *qdisc)
+{
+	free(qdisc->q_subdata);
+}
+
+static void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	double r, rbit, lim;
 	char *ru, *rubit, *limu;
 	struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
 
 	if (!tbf)
-		goto ignore;
+		return;
 
 	r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
 	rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
 	lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
 
-	dp_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
+	nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
 		r, ru, rbit, rubit, lim, limu);
-
-ignore:
-	return line;
 }
 
-static int tbf_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
-			 int line)
+static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
 {
 	struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
 
 	if (!tbf)
-		goto ignore;
+		return;
 
 	if (1) {
 		char *bu, *cu;
@@ -128,7 +128,7 @@
 		double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
 						 &cu);
 
-		dp_dump(p, "mpu %u rate-bucket-size %1.f%s "
+		nl_dump(p, "mpu %u rate-bucket-size %1.f%s "
 			   "rate-cell-size %.1f%s\n",
 			tbf->qt_mpu, bs, bu, cl, cu);
 
@@ -144,14 +144,11 @@
 		cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
 					 &clu);
 
-		dp_dump_line(p, line++, "    peak-rate %.2f%s/s (%.0f%s) "
-					"bucket-size %.1f%s cell-size %.1f%s",
-					"latency %.1f%s",
+		nl_dump_line(p, "    peak-rate %.2f%s/s (%.0f%s) "
+				"bucket-size %.1f%s cell-size %.1f%s"
+				"latency %.1f%s",
 			     pr, pru, prb, prbu, bs, bsu, cl, clu);
 	}
-
-ignore:
-	return line;
 }
 
 static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc)
@@ -226,7 +223,7 @@
 	
 	tbf = tbf_alloc(qdisc);
 	if (!tbf)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	tbf->qt_limit = limit;
 	tbf->qt_mask |= TBF_ATTR_LIMIT;
@@ -270,11 +267,10 @@
 
 	tbf = tbf_alloc(qdisc);
 	if (!tbf)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	if (!(tbf->qt_mask & TBF_ATTR_RATE))
-		return nl_error(EINVAL, "The rate must be specified before "
-				"limit can be calculated based on latency.");
+		return -NLE_MISSING_ATTR;
 
 	limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
 
@@ -301,8 +297,8 @@
 	tbf = tbf_qdisc(qdisc);
 	if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT))
 		return tbf->qt_limit;
-	return
-		nl_errno(ENOENT);
+	else
+		return -NLE_NOATTR;
 }
 
 /**
@@ -317,7 +313,7 @@
 	
 	tbf = tbf_alloc(qdisc);
 	if (!tbf)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	tbf->qt_mpu = mpu;
 	tbf->qt_mask |= TBF_ATTR_MPU;
@@ -337,8 +333,8 @@
 	tbf = tbf_qdisc(qdisc);
 	if (tbf && (tbf->qt_mask & TBF_ATTR_MPU))
 		return tbf->qt_mpu;
-	return
-		nl_errno(ENOENT);
+	else
+		return -NLE_NOATTR;
 }
 
 static inline int calc_cell_log(int cell, int bucket)
@@ -374,7 +370,7 @@
 	
 	tbf = tbf_alloc(qdisc);
 	if (!tbf)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	cell_log = calc_cell_log(cell, bucket);
 	if (cell_log < 0)
@@ -453,7 +449,7 @@
 	
 	tbf = tbf_alloc(qdisc);
 	if (!tbf)
-		return nl_errno(ENOMEM);
+		return -NLE_NOMEM;
 
 	cell_log = calc_cell_log(cell, bucket);
 	if (cell_log < 0)
@@ -522,8 +518,11 @@
 static struct rtnl_qdisc_ops tbf_qdisc_ops = {
 	.qo_kind		= "tbf",
 	.qo_msg_parser		= tbf_msg_parser,
-	.qo_dump[NL_DUMP_BRIEF]	= tbf_dump_brief,
-	.qo_dump[NL_DUMP_FULL]	= tbf_dump_full,
+	.qo_dump = {
+	    [NL_DUMP_LINE]	= tbf_dump_line,
+	    [NL_DUMP_DETAILS]	= tbf_dump_details,
+	},
+	.qo_free_data		= tbf_free_data,
 	.qo_get_opts		= tbf_get_opts,
 };
 
diff --git a/lib/route/tc.c b/lib/route/tc.c
index 1351fa2..97faef4 100644
--- a/lib/route/tc.c
+++ b/lib/route/tc.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
@@ -66,7 +66,7 @@
 		return err;
 
 	if (tb[TCA_KIND] == NULL)
-		return nl_error(EINVAL, "Missing tca kind TLV");
+		return -NLE_MISSING_ATTR;
 
 	nla_strlcpy(g->tc_kind, tb[TCA_KIND], TCKINDSIZ);
 
@@ -81,9 +81,9 @@
 		      TCA_ATTR_PARENT | TCA_ATTR_INFO | TCA_ATTR_KIND);
 
 	if (tb[TCA_OPTIONS]) {
-		g->tc_opts = nla_get_data(tb[TCA_OPTIONS]);
+		g->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]);
 		if (!g->tc_opts)
-			return nl_errno(ENOMEM);
+			return -NLE_NOMEM;
 		g->ce_mask |= TCA_ATTR_OPTS;
 	}
 	
@@ -126,9 +126,9 @@
 		g->ce_mask |= TCA_ATTR_STATS;
 		
 		if (tbs[TCA_STATS_APP]) {
-			g->tc_xstats = nla_get_data(tbs[TCA_STATS_APP]);
+			g->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]);
 			if (g->tc_xstats == NULL)
-				return -ENOMEM;
+				return -NLE_NOMEM;
 		} else
 			goto compat_xstats;
 	} else {
@@ -149,9 +149,9 @@
 
 compat_xstats:
 		if (tb[TCA_XSTATS]) {
-			g->tc_xstats = nla_get_data(tb[TCA_XSTATS]);
+			g->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]);
 			if (g->tc_xstats == NULL)
-				return -ENOMEM;
+				return -NLE_NOMEM;
 			g->ce_mask |= TCA_ATTR_XSTATS;
 		}
 	}
@@ -171,58 +171,53 @@
 	if (src->tc_opts) {
 		dst->tc_opts = nl_data_clone(src->tc_opts);
 		if (!dst->tc_opts)
-			goto errout;
+			return -NLE_NOMEM;
 	}
 	
 	if (src->tc_xstats) {
 		dst->tc_xstats = nl_data_clone(src->tc_xstats);
 		if (!dst->tc_xstats)
-			goto errout;
+			return -NLE_NOMEM;
 	}
 
 	return 0;
-errout:
-	return nl_get_errno();
 }
 
-int tca_dump_brief(struct rtnl_tca *g, const char *type,
-		   struct nl_dump_params *p, int line)
+void tca_dump_line(struct rtnl_tca *g, const char *type,
+		   struct nl_dump_params *p)
 {
 	char handle[32], parent[32];
 	struct nl_cache *link_cache;
 	
 	link_cache = nl_cache_mngt_require("route/link");
 
-	dp_dump(p, "%s %s ", g->tc_kind, type);
+	nl_dump_line(p, "%s %s ", g->tc_kind, type);
 
 	if (link_cache) {
 		char buf[32];
-		dp_dump(p, "dev %s ",
+		nl_dump(p, "dev %s ",
 			rtnl_link_i2name(link_cache, g->tc_ifindex,
 					 buf, sizeof(buf)));
 	} else
-		dp_dump(p, "dev %u ", g->tc_ifindex);
+		nl_dump(p, "dev %u ", g->tc_ifindex);
 	
-	dp_dump(p, "handle %s parent %s",
+	nl_dump(p, "handle %s parent %s",
 		rtnl_tc_handle2str(g->tc_handle, handle, sizeof(handle)),
 		rtnl_tc_handle2str(g->tc_parent, parent, sizeof(parent)));
-
-	return 1;
 }
 
-int tca_dump_full(struct rtnl_tca *g, struct nl_dump_params *p, int line)
+void tca_dump_details(struct rtnl_tca *g, struct nl_dump_params *p)
 {
-	dp_dump_line(p, line++, "  ");
-	return line;
+	nl_dump_line(p, "  ");
 }
 
-int tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p, int line)
+void tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p)
 {
 	char *unit, fmt[64];
 	float res;
 	strcpy(fmt, "        %7.2f %s %10u %10u %10u %10u %10u\n");
 
-	dp_dump_line(p, line++,
+	nl_dump_line(p, 
 		"    Stats:    bytes    packets      drops overlimits" \
 		"       qlen    backlog\n");
 
@@ -230,7 +225,7 @@
 	if (*unit == 'B')
 		fmt[11] = '9';
 
-	dp_dump_line(p, line++, fmt, res, unit,
+	nl_dump_line(p, fmt, res, unit,
 		g->tc_stats[RTNL_TC_PACKETS],
 		g->tc_stats[RTNL_TC_DROPS],
 		g->tc_stats[RTNL_TC_OVERLIMITS],
@@ -244,9 +239,7 @@
 	if (*unit == 'B')
 		fmt[11] = '9';
 
-	dp_dump_line(p, line++, fmt, res, unit, g->tc_stats[RTNL_TC_RATE_PPS]);
-
-	return line;
+	nl_dump_line(p, fmt, res, unit, g->tc_stats[RTNL_TC_RATE_PPS]);
 }
 
 int tca_compare(struct nl_object *_a, struct nl_object *_b,
@@ -276,10 +269,7 @@
 
 int tca_get_ifindex(struct rtnl_tca *t)
 {
-	if (t->ce_mask & TCA_ATTR_IFINDEX)
-		return t->tc_ifindex;
-	else
-		return RTNL_LINK_NOT_FOUND;
+	return t->tc_ifindex;
 }
 
 void tca_set_handle(struct rtnl_tca *t, uint32_t handle)
@@ -332,7 +322,8 @@
 	return t->tc_stats[id];
 }
 
-struct nl_msg *tca_build_msg(struct rtnl_tca *tca, int type, int flags)
+int tca_build_msg(struct rtnl_tca *tca, int type, int flags,
+		  struct nl_msg **result)
 {
 	struct nl_msg *msg;
 	struct tcmsg tchdr = {
@@ -344,7 +335,7 @@
 
 	msg = nlmsg_alloc_simple(type, flags);
 	if (!msg)
-		goto nla_put_failure;
+		return -NLE_NOMEM;
 
 	if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
 		goto nla_put_failure;
@@ -352,11 +343,12 @@
 	if (tca->ce_mask & TCA_ATTR_KIND)
 	    NLA_PUT_STRING(msg, TCA_KIND, tca->tc_kind);
 
-	return msg;
+	*result = msg;
+	return 0;
 
 nla_put_failure:
 	nlmsg_free(msg);
-	return NULL;
+	return -NLE_MSGSIZE;
 }
 
 /** @endcond */
@@ -425,7 +417,7 @@
 		if ((1 << i) == cell_size)
 			return i;
 
-	return nl_errno(EINVAL);
+	return -NLE_INVAL;
 }
 
 
@@ -546,13 +538,13 @@
 		/* :YYYY */
 		h = 0;
 		if (':' != *colon)
-			return -EINVAL;
+			return -NLE_INVAL;
 	}
 
 	if (':' == *colon) {
 		/* check if we would lose bits */
 		if (TC_H_MAJ(h))
-			return -ERANGE;
+			return -NLE_RANGE;
 		h <<= 16;
 
 		if ('\0' == colon[1]) {
@@ -564,10 +556,10 @@
 
 			/* check if we overlap with major part */
 			if (TC_H_MAJ(l))
-				return -ERANGE;
+				return -NLE_RANGE;
 
 			if ('\0' != *end)
-				return -EINVAL;
+				return -NLE_INVAL;
 
 			*res = (h | l);
 		}
@@ -575,7 +567,7 @@
 		/* XXXXYYYY */
 		*res = h;
 	} else
-		return -EINVAL;
+		return -NLE_INVAL;
 
 	return 0;
 }
diff --git a/lib/socket.c b/lib/socket.c
index aae8f54..8083bbb 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -1,91 +1,17 @@
 /*
- * lib/socket.c		Netlink Socket Handle
+ * lib/socket.c		Netlink Socket
  *
  *	This library is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU Lesser General Public
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
- * @ingroup nl
+ * @ingroup core
  * @defgroup socket Socket
- * @brief Handle representing a netlink socket.
- *
- * The socket is represented in a structure called the netlink handle,
- * besides the socket, it stores various settings and values related
- * to the socket. Every socket handle has a mandatory association with
- * a set of callbacks which can be used to modify the behaviour when
- * sending/receiving data from the socket.
- *
- * @par Socket Attributes
- * - \b Local \b Port: The local port is a netlink port identifying the
- *   local endpoint. It is used as source address for outgoing messages
- *   and will be addressed in replies. It must therefore be unique among
- *   all userspace applications. When the socket handle is allocated, a
- *   unique port number is generated automatically in the form of 22 bits
- *   Process Identifier + 10 bits Arbitary Number. Therefore the library
- *   is capable of generating 1024 unique local port numbers for every
- *   process. If more sockets are required, the application has to manage
- *   port numbers itself using nl_socket_set_local_port().
- * - \b Group \b Subscriptions: A socket can subscribe to any number of
- *   multicast groups. It will then receive a copy of all messages sent
- *   to one of the groups. This method is mainly used for event notification.
- *   Prior to kernel 2.6.14, the group subscription was done via bitmask
- *   which limited to a total number of groups of 32. With 2.6.14 a new
- *   method was added based on continous identifiers which supports an
- *   arbitary number of groups. Both methods are supported, see
- *   nl_join_groups() respectively nl_socket_add_membership() and
- *   nl_socket_drop_membership().
- * - \b Peer \b Port: The peer port is a netlink port identifying the
- *   peer's endpoint. If no peer port is specified, the kernel will try to
- *   autobind to a socket of the specified netlink family automatically.
- *   This is very common as typically only one listening socket exists
- *   on the kernel side. The peer port can be modified using
- *   nl_socket_set_peer_port().
- * - \b Peer \b Groups:
- * - \b File \b Descriptor: The file descriptor of the socket, it can be
- *   accessed via nl_socket_get_fd() to change socket options or monitor
- *   activity using poll()/select().
- * - \b Protocol: Once connected, the socket is bound to stick to one
- *   netlink family. This field is invisible, it is maintained automatically.
- *   (See nl_connect())
- * - \b Next \b Sequence \b Number: Next available sequence number to be used
- *   for the next message being sent out. (Initial value: UNIX time when the
- *   socket was allocated.) Sequence numbers can be used via
- *   nl_socket_use_seq().
- * - \b Expected \b Sequence \b Number: Expected sequence number in the next
- *   message received from the socket. (Initial value: Equal to next sequence
- *   number.)
- * - \b Callbacks \b Configuration:
- *
- * @par 1) Creating the netlink handle
- * @code
- * struct nl_handle *handle;
- *
- * // Allocate and initialize a new netlink handle
- * handle = nl_handle_alloc();
- *
- * // Use nl_socket_get_fd() to fetch the file description, for example to
- * // put a socket into non-blocking i/o mode.
- * fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK);
- * @endcode
- *
- * @par 2) Group Subscriptions
- * @code
- * // Event notifications are typically sent to multicast addresses which
- * // represented by groups. Join a group to f.e. receive link notifications.
- * nl_socket_add_membership(handle, RTNLGRP_LINK);
- * @endcode
- *
- * @par 6) Cleaning up
- * @code
- * // Finally destroy the netlink handle
- * nl_handle_destroy(handle);
- * @endcode
- * 
  * @{
  */
 
@@ -153,7 +79,7 @@
 		return;
 	
 	nr = port >> 22;
-	used_ports_map[nr / 32] &= ~((nr % 32) + 1);
+	used_ports_map[nr / 32] &= ~(1 << nr % 32);
 }
 
 /**
@@ -161,83 +87,78 @@
  * @{
  */
 
-static struct nl_handle *__alloc_handle(struct nl_cb *cb)
+static struct nl_sock *__alloc_socket(struct nl_cb *cb)
 {
-	struct nl_handle *handle;
+	struct nl_sock *sk;
 
-	handle = calloc(1, sizeof(*handle));
-	if (!handle) {
-		nl_errno(ENOMEM);
+	sk = calloc(1, sizeof(*sk));
+	if (!sk)
+		return NULL;
+
+	sk->s_fd = -1;
+	sk->s_cb = cb;
+	sk->s_local.nl_family = AF_NETLINK;
+	sk->s_peer.nl_family = AF_NETLINK;
+	sk->s_seq_expect = sk->s_seq_next = time(0);
+	sk->s_local.nl_pid = generate_local_port();
+	if (sk->s_local.nl_pid == UINT_MAX) {
+		nl_socket_free(sk);
 		return NULL;
 	}
 
-	handle->h_fd = -1;
-	handle->h_cb = cb;
-	handle->h_local.nl_family = AF_NETLINK;
-	handle->h_peer.nl_family = AF_NETLINK;
-	handle->h_seq_expect = handle->h_seq_next = time(0);
-	handle->h_local.nl_pid = generate_local_port();
-	if (handle->h_local.nl_pid == UINT_MAX) {
-		nl_handle_destroy(handle);
-		nl_error(ENOBUFS, "Out of local ports");
-		return NULL;
-	}
-
-	return handle;
+	return sk;
 }
 
 /**
- * Allocate new netlink socket handle.
+ * Allocate new netlink socket
  *
- * @return Newly allocated netlink socket handle or NULL.
+ * @return Newly allocated netlink socket or NULL.
  */
-struct nl_handle *nl_handle_alloc(void)
+struct nl_sock *nl_socket_alloc(void)
 {
 	struct nl_cb *cb;
 	
 	cb = nl_cb_alloc(default_cb);
-	if (!cb) {
-		nl_errno(ENOMEM);
+	if (!cb)
 		return NULL;
-	}
 
-	return __alloc_handle(cb);
+	return __alloc_socket(cb);
 }
 
 /**
- * Allocate new socket handle with custom callbacks
+ * Allocate new socket with custom callbacks
  * @arg cb		Callback handler
  *
  * The reference to the callback handler is taken into account
- * automatically, it is released again upon calling nl_handle_destroy().
+ * automatically, it is released again upon calling nl_socket_free().
  *
  *@return Newly allocted socket handle or NULL.
  */
-struct nl_handle *nl_handle_alloc_cb(struct nl_cb *cb)
+struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb)
 {
 	if (cb == NULL)
 		BUG();
 
-	return __alloc_handle(nl_cb_get(cb));
+	return __alloc_socket(nl_cb_get(cb));
 }
 
 /**
- * Destroy netlink handle.
- * @arg handle		Netlink handle.
+ * Free a netlink socket.
+ * @arg sk		Netlink socket.
  */
-void nl_handle_destroy(struct nl_handle *handle)
+void nl_socket_free(struct nl_sock *sk)
 {
-	if (!handle)
+	if (!sk)
 		return;
 
-	if (handle->h_fd >= 0)
-		close(handle->h_fd);
+	if (sk->s_fd >= 0)
+		close(sk->s_fd);
 
-	if (!(handle->h_flags & NL_OWN_PORT))
-		release_local_port(handle->h_local.nl_pid);
+	if (!(sk->s_flags & NL_OWN_PORT))
+		release_local_port(sk->s_local.nl_pid);
 
-	nl_cb_put(handle->h_cb);
-	free(handle);
+	nl_cb_put(sk->s_cb);
+	free(sk);
 }
 
 /** @} */
@@ -255,33 +176,60 @@
 
 /**
  * Disable sequence number checking.
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  *
- * Disables checking of sequence numbers on the netlink handle. This is
+ * Disables checking of sequence numbers on the netlink socket This is
  * required to allow messages to be processed which were not requested by
  * a preceding request message, e.g. netlink events.
  *
  * @note This function modifies the NL_CB_SEQ_CHECK configuration in
  * the callback handle associated with the socket.
  */
-void nl_disable_sequence_check(struct nl_handle *handle)
+void nl_socket_disable_seq_check(struct nl_sock *sk)
 {
-	nl_cb_set(handle->h_cb, NL_CB_SEQ_CHECK,
+	nl_cb_set(sk->s_cb, NL_CB_SEQ_CHECK,
 		  NL_CB_CUSTOM, noop_seq_check, NULL);
 }
 
 /**
  * Use next sequence number
- * @arg handle		Netlink handle
+ * @arg sk		Netlink socket.
  *
  * Uses the next available sequence number and increases the counter
  * by one for subsequent calls.
  *
  * @return Unique serial sequence number
  */
-unsigned int nl_socket_use_seq(struct nl_handle *handle)
+unsigned int nl_socket_use_seq(struct nl_sock *sk)
 {
-	return handle->h_seq_next++;
+	return sk->s_seq_next++;
+}
+
+/**
+ * Disable automatic request for ACK
+ * @arg sk		Netlink socket.
+ *
+ * The default behaviour of a socket is to request an ACK for
+ * each message sent to allow for the caller to synchronize to
+ * the completion of the netlink operation. This function
+ * disables this behaviour and will result in requests being
+ * sent which will not have the NLM_F_ACK flag set automatically.
+ * However, it is still possible for the caller to set the
+ * NLM_F_ACK flag explicitely.
+ */
+void nl_socket_disable_auto_ack(struct nl_sock *sk)
+{
+	sk->s_flags |= NL_NO_AUTO_ACK;
+}
+
+/**
+ * Enable automatic request for ACK (default)
+ * @arg sk		Netlink socket.
+ * @see nl_socket_disable_auto_ack
+ */
+void nl_socket_enable_auto_ack(struct nl_sock *sk)
+{
+	sk->s_flags &= ~NL_NO_AUTO_ACK;
 }
 
 /** @} */
@@ -291,31 +239,31 @@
  * @{
  */
 
-uint32_t nl_socket_get_local_port(struct nl_handle *handle)
+uint32_t nl_socket_get_local_port(struct nl_sock *sk)
 {
-	return handle->h_local.nl_pid;
+	return sk->s_local.nl_pid;
 }
 
 /**
  * Set local port of socket
- * @arg handle		Netlink handle
+ * @arg sk		Netlink socket.
  * @arg port		Local port identifier
  *
  * Assigns a local port identifier to the socket. If port is 0
  * a unique port identifier will be generated automatically.
  */
-void nl_socket_set_local_port(struct nl_handle *handle, uint32_t port)
+void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port)
 {
 	if (port == 0) {
 		port = generate_local_port(); 
-		handle->h_flags &= ~NL_OWN_PORT;
+		sk->s_flags &= ~NL_OWN_PORT;
 	} else  {
-		if (!(handle->h_flags & NL_OWN_PORT))
-			release_local_port(handle->h_local.nl_pid);
-		handle->h_flags |= NL_OWN_PORT;
+		if (!(sk->s_flags & NL_OWN_PORT))
+			release_local_port(sk->s_local.nl_pid);
+		sk->s_flags |= NL_OWN_PORT;
 	}
 
-	handle->h_local.nl_pid = port;
+	sk->s_local.nl_pid = port;
 }
 
 /** @} */
@@ -326,13 +274,14 @@
  */
 
 /**
- * Join a group
- * @arg handle		Netlink handle
+ * Join groups
+ * @arg sk		Netlink socket
  * @arg group		Group identifier
  *
- * Joins the specified group using the modern socket option which
+ * Joins the specified groups using the modern socket option which
  * is available since kernel version 2.6.14. It allows joining an
- * almost arbitary number of groups without limitation.
+ * almost arbitary number of groups without limitation.  The list
+ * of groups has to be terminated by 0 (%NFNLGRP_NONE).
  *
  * Make sure to use the correct group definitions as the older
  * bitmask definitions for nl_join_groups() are likely to still
@@ -340,61 +289,95 @@
  *
  * @return 0 on sucess or a negative error code.
  */
-int nl_socket_add_membership(struct nl_handle *handle, int group)
+int nl_socket_add_memberships(struct nl_sock *sk, int group, ...)
 {
 	int err;
+	va_list ap;
 
-	if (handle->h_fd == -1)
-		return nl_error(EBADFD, "Socket not connected");
+	if (sk->s_fd == -1)
+		return -NLE_BAD_SOCK;
 
-	err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
-			 &group, sizeof(group));
-	if (err < 0)
-		return nl_error(errno, "setsockopt(NETLINK_ADD_MEMBERSHIP) "
-				       "failed");
+	va_start(ap, group);
+
+	while (group != 0) {
+		if (group < 0)
+			return -NLE_INVAL;
+
+		err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
+						 &group, sizeof(group));
+		if (err < 0)
+			return -nl_syserr2nlerr(errno);
+
+		group = va_arg(ap, int);
+	}
+
+	va_end(ap);
 
 	return 0;
 }
 
+int nl_socket_add_membership(struct nl_sock *sk, int group)
+{
+	return nl_socket_add_memberships(sk, group, 0);
+}
+
 /**
- * Leave a group
- * @arg handle		Netlink handle
+ * Leave groups
+ * @arg sk		Netlink socket
  * @arg group		Group identifier
  *
- * Leaves the specified group using the modern socket option
- * which is available since kernel version 2.6.14.
+ * Leaves the specified groups using the modern socket option
+ * which is available since kernel version 2.6.14. The list of groups
+ * has to terminated by 0 (%NFNLGRP_NONE).
  *
  * @see nl_socket_add_membership
  * @return 0 on success or a negative error code.
  */
-int nl_socket_drop_membership(struct nl_handle *handle, int group)
+int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...)
 {
 	int err;
+	va_list ap;
 
-	if (handle->h_fd == -1)
-		return nl_error(EBADFD, "Socket not connected");
+	if (sk->s_fd == -1)
+		return -NLE_BAD_SOCK;
 
-	err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
-			 &group, sizeof(group));
-	if (err < 0)
-		return nl_error(errno, "setsockopt(NETLINK_DROP_MEMBERSHIP) "
-				       "failed");
+	va_start(ap, group);
+
+	while (group != 0) {
+		if (group < 0)
+			return -NLE_INVAL;
+
+		err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
+						 &group, sizeof(group));
+		if (err < 0)
+			return -nl_syserr2nlerr(errno);
+
+		group = va_arg(ap, int);
+	}
+
+	va_end(ap);
 
 	return 0;
 }
 
+int nl_socket_drop_membership(struct nl_sock *sk, int group)
+{
+	return nl_socket_drop_memberships(sk, group, 0);
+}
+
+
 /**
  * Join multicast groups (deprecated)
- * @arg handle		Netlink handle.
+ * @arg sk		Netlink socket.
  * @arg groups		Bitmask of groups to join.
  *
  * This function defines the old way of joining multicast group which
  * has to be done prior to calling nl_connect(). It works on any kernel
  * version but is very limited as only 32 groups can be joined.
  */
-void nl_join_groups(struct nl_handle *handle, int groups)
+void nl_join_groups(struct nl_sock *sk, int groups)
 {
-	handle->h_local.nl_groups |= groups;
+	sk->s_local.nl_groups |= groups;
 }
 
 
@@ -405,14 +388,14 @@
  * @{
  */
 
-uint32_t nl_socket_get_peer_port(struct nl_handle *handle)
+uint32_t nl_socket_get_peer_port(struct nl_sock *sk)
 {
-	return handle->h_peer.nl_pid;
+	return sk->s_peer.nl_pid;
 }
 
-void nl_socket_set_peer_port(struct nl_handle *handle, uint32_t port)
+void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port)
 {
-	handle->h_peer.nl_pid = port;
+	sk->s_peer.nl_pid = port;
 }
 
 /** @} */
@@ -422,44 +405,44 @@
  * @{
  */
 
-int nl_socket_get_fd(struct nl_handle *handle)
+int nl_socket_get_fd(struct nl_sock *sk)
 {
-	return handle->h_fd;
+	return sk->s_fd;
 }
 
 /**
- * Set file descriptor of socket handle to non-blocking state
- * @arg handle		Netlink socket
+ * Set file descriptor of socket to non-blocking state
+ * @arg sk		Netlink socket.
  *
  * @return 0 on success or a negative error code.
  */
-int nl_socket_set_nonblocking(struct nl_handle *handle)
+int nl_socket_set_nonblocking(struct nl_sock *sk)
 {
-	if (handle->h_fd == -1)
-		return nl_error(EBADFD, "Socket not connected");
+	if (sk->s_fd == -1)
+		return -NLE_BAD_SOCK;
 
-	if (fcntl(handle->h_fd, F_SETFL, O_NONBLOCK) < 0)
-		return nl_error(errno, "fcntl(F_SETFL, O_NONBLOCK) failed");
+	if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0)
+		return -nl_syserr2nlerr(errno);
 
 	return 0;
 }
 
 /**
  * Enable use of MSG_PEEK when reading from socket
- * @arg handle		Netlink socket
+ * @arg sk		Netlink socket.
  */
-void nl_socket_enable_msg_peek(struct nl_handle *handle)
+void nl_socket_enable_msg_peek(struct nl_sock *sk)
 {
-	handle->h_flags |= NL_MSG_PEEK;
+	sk->s_flags |= NL_MSG_PEEK;
 }
 
 /**
  * Disable use of MSG_PEEK when reading from socket
- * @arg handle		Netlink socket
+ * @arg sk		Netlink socket.
  */
-void nl_socket_disable_msg_peek(struct nl_handle *handle)
+void nl_socket_disable_msg_peek(struct nl_sock *sk)
 {
-	handle->h_flags &= ~NL_MSG_PEEK;
+	sk->s_flags &= ~NL_MSG_PEEK;
 }
 
 /** @} */
@@ -469,20 +452,20 @@
  * @{
  */
 
-struct nl_cb *nl_socket_get_cb(struct nl_handle *handle)
+struct nl_cb *nl_socket_get_cb(struct nl_sock *sk)
 {
-	return nl_cb_get(handle->h_cb);
+	return nl_cb_get(sk->s_cb);
 }
 
-void nl_socket_set_cb(struct nl_handle *handle, struct nl_cb *cb)
+void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb)
 {
-	nl_cb_put(handle->h_cb);
-	handle->h_cb = nl_cb_get(cb);
+	nl_cb_put(sk->s_cb);
+	sk->s_cb = nl_cb_get(cb);
 }
 
 /**
  * Modify the callback handler associated to the socket
- * @arg handle		netlink handle
+ * @arg sk		Netlink socket.
  * @arg type		which type callback to set
  * @arg kind		kind of callback
  * @arg func		callback function
@@ -490,11 +473,11 @@
  *
  * @see nl_cb_set
  */
-int nl_socket_modify_cb(struct nl_handle *handle, enum nl_cb_type type,
+int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type,
 			enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func,
 			void *arg)
 {
-	return nl_cb_set(handle->h_cb, type, kind, func, arg);
+	return nl_cb_set(sk->s_cb, type, kind, func, arg);
 }
 
 /** @} */
@@ -505,19 +488,19 @@
  */
 
 /**
- * Set socket buffer size of netlink handle.
- * @arg handle		Netlink handle.
+ * Set socket buffer size of netlink socket.
+ * @arg sk		Netlink socket.
  * @arg rxbuf		New receive socket buffer size in bytes.
  * @arg txbuf		New transmit socket buffer size in bytes.
  *
- * Sets the socket buffer size of a netlink handle to the specified
+ * Sets the socket buffer size of a netlink socket to the specified
  * values \c rxbuf and \c txbuf. Providing a value of \c 0 assumes a
  * good default value.
  *
  * @note It is not required to call this function prior to nl_connect().
  * @return 0 on sucess or a negative error code.
  */
-int nl_set_buffer_size(struct nl_handle *handle, int rxbuf, int txbuf)
+int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf)
 {
 	int err;
 
@@ -527,69 +510,69 @@
 	if (txbuf <= 0)
 		txbuf = 32768;
 
-	if (handle->h_fd == -1)
-		return nl_error(EBADFD, "Socket not connected");
+	if (sk->s_fd == -1)
+		return -NLE_BAD_SOCK;
 	
-	err = setsockopt(handle->h_fd, SOL_SOCKET, SO_SNDBUF,
+	err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF,
 			 &txbuf, sizeof(txbuf));
 	if (err < 0)
-		return nl_error(errno, "setsockopt(SO_SNDBUF) failed");
+		return -nl_syserr2nlerr(errno);
 
-	err = setsockopt(handle->h_fd, SOL_SOCKET, SO_RCVBUF,
+	err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF,
 			 &rxbuf, sizeof(rxbuf));
 	if (err < 0)
-		return nl_error(errno, "setsockopt(SO_RCVBUF) failed");
+		return -nl_syserr2nlerr(errno);
 
-	handle->h_flags |= NL_SOCK_BUFSIZE_SET;
+	sk->s_flags |= NL_SOCK_BUFSIZE_SET;
 
 	return 0;
 }
 
 /**
- * Enable/disable credential passing on netlink handle.
- * @arg handle		Netlink handle
+ * Enable/disable credential passing on netlink socket.
+ * @arg sk		Netlink socket.
  * @arg state		New state (0 - disabled, 1 - enabled)
  *
  * @return 0 on success or a negative error code
  */
-int nl_set_passcred(struct nl_handle *handle, int state)
+int nl_socket_set_passcred(struct nl_sock *sk, int state)
 {
 	int err;
 
-	if (handle->h_fd == -1)
-		return nl_error(EBADFD, "Socket not connected");
+	if (sk->s_fd == -1)
+		return -NLE_BAD_SOCK;
 
-	err = setsockopt(handle->h_fd, SOL_SOCKET, SO_PASSCRED,
+	err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED,
 			 &state, sizeof(state));
 	if (err < 0)
-		return nl_error(errno, "setsockopt(SO_PASSCRED) failed");
+		return -nl_syserr2nlerr(errno);
 
 	if (state)
-		handle->h_flags |= NL_SOCK_PASSCRED;
+		sk->s_flags |= NL_SOCK_PASSCRED;
 	else
-		handle->h_flags &= ~NL_SOCK_PASSCRED;
+		sk->s_flags &= ~NL_SOCK_PASSCRED;
 
 	return 0;
 }
 
 /**
  * Enable/disable receival of additional packet information
- * @arg handle		Netlink handle
+ * @arg sk		Netlink socket.
  * @arg state		New state (0 - disabled, 1 - enabled)
  *
  * @return 0 on success or a negative error code
  */
-int nl_socket_recv_pktinfo(struct nl_handle *handle, int state)
+int nl_socket_recv_pktinfo(struct nl_sock *sk, int state)
 {
 	int err;
 
-	if (handle->h_fd == -1)
-		return nl_error(EBADFD, "Socket not connected");
+	if (sk->s_fd == -1)
+		return -NLE_BAD_SOCK;
 
-	err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_PKTINFO,
+	err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO,
 			 &state, sizeof(state));
 	if (err < 0)
-		return nl_error(errno, "setsockopt(NETLINK_PKTINFO) failed");
+		return -nl_syserr2nlerr(errno);
 
 	return 0;
 }
diff --git a/lib/utils.c b/lib/utils.c
index b5b457a..e1fdae1 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -6,10 +6,11 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
 /**
+ * @ingroup core
  * @defgroup utils Utilities
  * @{
  */
@@ -25,7 +26,7 @@
 int nl_debug = 0;
 
 struct nl_dump_params nl_debug_dp = {
-	.dp_type = NL_DUMP_FULL,
+	.dp_type = NL_DUMP_DETAILS,
 };
 
 static void __init nl_debug_init(void)
@@ -41,48 +42,6 @@
 	nl_debug_dp.dp_fd = stderr;
 }
 
-/**
- * @name Error Code Helpers
- * @{
- */
-
-static char *errbuf;
-static int nlerrno;
-
-/** @cond SKIP */
-int __nl_error(int err, const char *file, unsigned int line, const char *func,
-	       const char *fmt, ...)
-{
-	char *user_err;
-	va_list args;
-
-	if (errbuf) {
-		free(errbuf);
-		errbuf = NULL;
-	}
-
-	nlerrno = err;
-
-	if (fmt) {
-		va_start(args, fmt);
-		vasprintf(&user_err, fmt, args);
-		va_end(args);
-	}
-
-#ifdef VERBOSE_ERRORS
-	asprintf(&errbuf, "%s:%u:%s: %s (errno = %s)",
-		 file, line, func, fmt ? user_err : "", strerror(err));
-#else
-	asprintf(&errbuf, "%s (errno = %s)",
-		 fmt ? user_err : "", strerror(err));
-#endif
-
-	if (fmt)
-		free(user_err);
-
-	return -err;
-}
-
 int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *))
 {
 	FILE *fd;
@@ -90,8 +49,7 @@
 
 	fd = fopen(path, "r");
 	if (fd == NULL)
-		return nl_error(errno, "Unable to open file %s for reading",
-				path);
+		return -nl_syserr2nlerr(errno);
 
 	while (fgets(buf, sizeof(buf), fd)) {
 		int goodlen, err;
@@ -103,17 +61,17 @@
 
 		num = strtol(buf, &end, 0);
 		if (end == buf)
-			return nl_error(EINVAL, "Parsing error");
+			return -NLE_INVAL;
 
 		if (num == LONG_MIN || num == LONG_MAX)
-			return nl_error(errno, "Number of out range");
+			return -NLE_RANGE;
 
 		while (*end == ' ' || *end == '\t')
 			end++;
 
 		goodlen = strcspn(end, "#\r\n\t ");
 		if (goodlen == 0)
-			return nl_error(EINVAL, "Empty string");
+			return -NLE_INVAL;
 
 		end[goodlen] = '\0';
 
@@ -127,49 +85,6 @@
 	return 0;
 }
 
-/** @endcond */
-
-int nl_get_errno(void)
-{
-	return nlerrno;
-}
-
-
-/**
- * Return error message for an error code
- * @return error message
- */
-char *nl_geterror(void)
-{
-	if (errbuf)
-		return errbuf;
-
-	if (nlerrno)
-		return strerror(nlerrno);
-
-	return "Sucess\n";
-}
-
-/**
- * Print a libnl error message
- * @arg s		error message prefix
- *
- * Prints the error message of the call that failed last.
- *
- * If s is not NULL and *s is not a null byte the argument
- * string is printed, followed by a colon and a blank. Then
- * the error message and a new-line.
- */
-void nl_perror(const char *s)
-{
-	if (s && *s)
-		fprintf(stderr, "%s: %s\n", s, nl_geterror());
-	else
-		fprintf(stderr, "%s\n", nl_geterror());
-}
-
-/** @} */
-
 /**
  * @name Unit Pretty-Printing
  * @{
@@ -285,7 +200,7 @@
 	char *p;
 	long l = strtol(str, &p, 0);
 	if (p == str)
-		return -1;
+		return -NLE_INVAL;
 
 	if (*p) {
 		if (!strcasecmp(p, "kb") || !strcasecmp(p, "k"))
@@ -303,7 +218,7 @@
 		else if (!strcasecmp(p, "bit"))
 			l /= 8;
 		else if (strcasecmp(p, "b") != 0)
-			return -1;
+			return -NLE_INVAL;
 	}
 
 	return l;
@@ -328,16 +243,16 @@
 	double d = strtod(str, &p);
 
 	if (p == str)
-		return -1;
+		return -NLE_INVAL;
 
 	if (d > 1.0)
 		d /= 100.0f;
 
 	if (d > 1.0f || d < 0.0f)
-		return -1;
+		return -NLE_RANGE;
 
 	if (*p && strcmp(p, "%") != 0)
-		return -1;
+		return -NLE_INVAL;
 
 	return rint(d * NL_PROB_MAX);
 }
@@ -370,7 +285,7 @@
 {
 	char name[FILENAME_MAX];
 	FILE *fd;
-	int got_hz = 0, got_tick = 0;
+	int got_hz = 0;
 
 	if (getenv("HZ")) {
 		long hz = strtol(getenv("HZ"), NULL, 0);
@@ -386,28 +301,25 @@
 
 	if (getenv("TICKS_PER_USEC")) {
 		double t = strtod(getenv("TICKS_PER_USEC"), NULL);
-
 		ticks_per_usec = t;
-		got_tick = 1;
 	}
+	else {
+		if (getenv("PROC_NET_PSCHED"))
+			snprintf(name, sizeof(name), "%s", getenv("PROC_NET_PSCHED"));
+		else if (getenv("PROC_ROOT"))
+			snprintf(name, sizeof(name), "%s/net/psched",
+				 getenv("PROC_ROOT"));
+		else
+			strncpy(name, "/proc/net/psched", sizeof(name) - 1);
 		
-
-	if (getenv("PROC_NET_PSCHED"))
-		snprintf(name, sizeof(name), "%s", getenv("PROC_NET_PSCHED"));
-	else if (getenv("PROC_ROOT"))
-		snprintf(name, sizeof(name), "%s/net/psched",
-			 getenv("PROC_ROOT"));
-	else
-		strncpy(name, "/proc/net/psched", sizeof(name) - 1);
-
-	if ((fd = fopen(name, "r"))) {
-		uint32_t tick, us, nom;
-		int r = fscanf(fd, "%08x%08x%08x%*08x", &tick, &us, &nom);
-
-		if (4 == r && nom == 1000000 && !got_tick)
+		if ((fd = fopen(name, "r"))) {
+			uint32_t tick, us;
+			/* the file contains 4 hexadecimals, but we just use
+			   the first two of them */
+			fscanf(fd, "%08x %08x", &tick, &us);
 			ticks_per_usec = (double)tick/(double)us;
-			
-		fclose(fd);
+			fclose(fd);
+		}
 	}
 }
 
@@ -442,25 +354,40 @@
 	return ticks / ticks_per_usec;
 }
 
-long nl_time2int(const char *str)
+int nl_str2msec(const char *str, uint64_t *result)
 {
+	uint64_t total = 0, l;
+	int plen;
 	char *p;
-	long l = strtol(str, &p, 0);
-	if (p == str)
-		return -1;
 
-	if (*p) {
-		if (!strcasecmp(p, "min") == 0 || !strcasecmp(p, "m"))
-			l *= 60;
-		else if (!strcasecmp(p, "hour") || !strcasecmp(p, "h"))
-			l *= 60*60;
-		else if (!strcasecmp(p, "day") || !strcasecmp(p, "d"))
-			l *= 60*60*24;
-		else if (strcasecmp(p, "s") != 0)
-			return -1;
-	}
+	do {
+		l = strtoul(str, &p, 0);
+		if (p == str)
+			return -NLE_INVAL;
+		else if (*p) {
+			plen = strcspn(p, " \t");
 
-	return l;
+			if (!plen)
+				total += l;
+			else if (!strncasecmp(p, "sec", plen))
+				total += (l * 1000);
+			else if (!strncasecmp(p, "min", plen))
+				total += (l * 1000*60);
+			else if (!strncasecmp(p, "hour", plen))
+				total += (l * 1000*60*60);
+			else if (!strncasecmp(p, "day", plen))
+				total += (l * 1000*60*60*24);
+			else
+				return -NLE_INVAL;
+
+			str = p + plen;
+		} else
+			total += l;
+	} while (*str && *p);
+
+	*result = total;
+
+	return 0;
 }
 
 /**
@@ -505,6 +432,47 @@
 /** @} */
 
 /**
+ * @name Netlink Family Translations
+ * @{
+ */
+
+static struct trans_tbl nlfamilies[] = {
+	__ADD(NETLINK_ROUTE,route)
+	__ADD(NETLINK_USERSOCK,usersock)
+	__ADD(NETLINK_FIREWALL,firewall)
+	__ADD(NETLINK_INET_DIAG,inetdiag)
+	__ADD(NETLINK_NFLOG,nflog)
+	__ADD(NETLINK_XFRM,xfrm)
+	__ADD(NETLINK_SELINUX,selinux)
+	__ADD(NETLINK_ISCSI,iscsi)
+	__ADD(NETLINK_AUDIT,audit)
+	__ADD(NETLINK_FIB_LOOKUP,fib_lookup)
+	__ADD(NETLINK_CONNECTOR,connector)
+	__ADD(NETLINK_NETFILTER,netfilter)
+	__ADD(NETLINK_IP6_FW,ip6_fw)
+	__ADD(NETLINK_DNRTMSG,dnrtmsg)
+	__ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent)
+	__ADD(NETLINK_GENERIC,generic)
+	__ADD(NETLINK_SCSITRANSPORT,scsitransport)
+	__ADD(NETLINK_ECRYPTFS,ecryptfs)
+};
+
+char * nl_nlfamily2str(int family, char *buf, size_t size)
+{
+	return __type2str(family, buf, size, nlfamilies,
+			  ARRAY_SIZE(nlfamilies));
+}
+
+int nl_str2nlfamily(const char *name)
+{
+	return __str2type(name, nlfamilies, ARRAY_SIZE(nlfamilies));
+}
+
+/**
+ * @}
+ */
+
+/**
  * @name Link Layer Protocol Translations
  * @{
  */
@@ -699,7 +667,7 @@
 
 	l = strtoul(name, &end, 0);
 	if (l == ULONG_MAX || *end != '\0')
-		return -1;
+		return -NLE_OBJ_NOTFOUND;
 
 	return (int) l;
 }
@@ -714,7 +682,6 @@
 /**
  * Handle a new line while dumping
  * @arg params		Dumping parameters
- * @arg line		Number of lines dumped already.
  *
  * This function must be called before dumping any onto a
  * new line. It will ensure proper prefixing as specified
@@ -722,8 +689,10 @@
  *
  * @note This function will NOT dump any newlines itself
  */
-void nl_new_line(struct nl_dump_params *params, int line)
+void nl_new_line(struct nl_dump_params *params)
 {
+	params->dp_line++;
+
 	if (params->dp_prefix) {
 		int i;
 		for (i = 0; i < params->dp_prefix; i++) {
@@ -737,9 +706,27 @@
 	}
 
 	if (params->dp_nl_cb)
-		params->dp_nl_cb(params, line);
+		params->dp_nl_cb(params, params->dp_line);
 }
 
+static void dump_one(struct nl_dump_params *parms, const char *fmt,
+		     va_list args)
+{
+	if (parms->dp_fd)
+		vfprintf(parms->dp_fd, fmt, args);
+	else if (parms->dp_buf || parms->dp_cb) {
+		char *buf = NULL;
+		vasprintf(&buf, fmt, args);
+		if (parms->dp_cb)
+			parms->dp_cb(parms, buf);
+		else
+			strncat(parms->dp_buf, buf,
+			        parms->dp_buflen - strlen(parms->dp_buf) - 1);
+		free(buf);
+	}
+}
+
+
 /**
  * Dump a formatted character string
  * @arg params		Dumping parameters
@@ -754,10 +741,195 @@
 	va_list args;
 
 	va_start(args, fmt);
-	__dp_dump(params, fmt, args);
+	dump_one(params, fmt, args);
 	va_end(args);
 }
 
+void nl_dump_line(struct nl_dump_params *parms, const char *fmt, ...)
+{
+	va_list args;
+
+	nl_new_line(parms);
+
+	va_start(args, fmt);
+	dump_one(parms, fmt, args);
+	va_end(args);
+}
+
+
 /** @} */
 
+/** @cond SKIP */
+
+int __trans_list_add(int i, const char *a, struct nl_list_head *head)
+{
+	struct trans_list *tl;
+
+	tl = calloc(1, sizeof(*tl));
+	if (!tl)
+		return -NLE_NOMEM;
+
+	tl->i = i;
+	tl->a = strdup(a);
+
+	nl_list_add_tail(&tl->list, head);
+
+	return 0;
+}
+
+void __trans_list_clear(struct nl_list_head *head)
+{
+	struct trans_list *tl, *next;
+
+	nl_list_for_each_entry_safe(tl, next, head, list) {
+		free(tl->a);
+		free(tl);
+	}
+}
+
+char *__type2str(int type, char *buf, size_t len, struct trans_tbl *tbl,
+		 size_t tbl_len)
+{
+	int i;
+	for (i = 0; i < tbl_len; i++) {
+		if (tbl[i].i == type) {
+			snprintf(buf, len, "%s", tbl[i].a);
+			return buf;
+		}
+	}
+
+	snprintf(buf, len, "0x%x", type);
+	return buf;
+}
+
+char *__list_type2str(int type, char *buf, size_t len,
+		      struct nl_list_head *head)
+{
+	struct trans_list *tl;
+
+	nl_list_for_each_entry(tl, head, list) {
+		if (tl->i == type) {
+			snprintf(buf, len, "%s", tl->a);
+			return buf;
+		}
+	}
+
+	snprintf(buf, len, "0x%x", type);
+	return buf;
+}
+
+char *__flags2str(int flags, char *buf, size_t len,
+		  struct trans_tbl *tbl, size_t tbl_len)
+{
+	int i;
+	int tmp = flags;
+
+	memset(buf, 0, len);
+	
+	for (i = 0; i < tbl_len; i++) {
+		if (tbl[i].i & tmp) {
+			tmp &= ~tbl[i].i;
+			strncat(buf, tbl[i].a, len - strlen(buf) - 1);
+			if ((tmp & flags))
+				strncat(buf, ",", len - strlen(buf) - 1);
+		}
+	}
+
+	return buf;
+}
+
+int __str2type(const char *buf, struct trans_tbl *tbl, size_t tbl_len)
+{
+	unsigned long l;
+	char *end;
+	int i;
+
+	if (*buf == '\0')
+		return -NLE_INVAL;
+
+	for (i = 0; i < tbl_len; i++)
+		if (!strcasecmp(tbl[i].a, buf))
+			return tbl[i].i;
+
+	l = strtoul(buf, &end, 0);
+	if (l == ULONG_MAX || *end != '\0')
+		return -NLE_OBJ_NOTFOUND;
+
+	return (int) l;
+}
+
+int __list_str2type(const char *buf, struct nl_list_head *head)
+{
+	struct trans_list *tl;
+	unsigned long l;
+	char *end;
+
+	if (*buf == '\0')
+		return -NLE_INVAL;
+
+	nl_list_for_each_entry(tl, head, list) {
+		if (!strcasecmp(tl->a, buf))
+			return tl->i;
+	}
+
+	l = strtoul(buf, &end, 0);
+	if (l == ULONG_MAX || *end != '\0')
+		return -NLE_OBJ_NOTFOUND;
+
+	return (int) l;
+}
+
+int __str2flags(const char *buf, struct trans_tbl *tbl, size_t tbl_len)
+{
+	int i, flags = 0, len;
+	char *p = (char *) buf, *t;
+
+	for (;;) {
+		if (*p == ' ')
+			p++;
+	
+		t = strchr(p, ',');
+		len = t ? t - p : strlen(p);
+		for (i = 0; i < tbl_len; i++)
+			if (!strncasecmp(tbl[i].a, p, len))
+				flags |= tbl[i].i;
+
+		if (!t)
+			return flags;
+
+		p = ++t;
+	}
+
+	return 0;
+}
+
+void dump_from_ops(struct nl_object *obj, struct nl_dump_params *params)
+{
+	int type = params->dp_type;
+
+	if (type < 0 || type > NL_DUMP_MAX)
+		BUG();
+
+	params->dp_line = 0;
+
+	if (params->dp_dump_msgtype) {
+#if 0
+		/* XXX */
+		char buf[64];
+
+		dp_dump_line(params, 0, "%s ",
+			     nl_cache_mngt_type2name(obj->ce_ops,
+			     			     obj->ce_ops->co_protocol,
+						     obj->ce_msgtype,
+						     buf, sizeof(buf)));
+#endif
+		params->dp_pre_dump = 1;
+	}
+
+	if (obj->ce_ops->oo_dump[type])
+		obj->ce_ops->oo_dump[type](obj, params);
+}
+
+/** @endcond */
+
 /** @} */
diff --git a/libnl-1.pc.in b/libnl-2.0.pc.in
similarity index 66%
rename from libnl-1.pc.in
rename to libnl-2.0.pc.in
index 7ac8413..e44f0fb 100644
--- a/libnl-1.pc.in
+++ b/libnl-2.0.pc.in
@@ -1,10 +1,10 @@
 prefix=@prefix@
-exec_prefix=@prefix@
+exec_prefix=@exec_prefix@
 libdir=@libdir@
-includedir=@prefix@/include
+includedir=@includedir@
 
 Name: libnl
 Description: Convenience library for netlink sockets
 Version: @PACKAGE_VERSION@
 Libs: -L${libdir} -lnl
-Cflags:
+Cflags: -I${includedir}
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644
index 0000000..8d0c756
--- /dev/null
+++ b/m4/.gitignore
@@ -0,0 +1,2 @@
+/lt*.m4
+/libtool.m4
diff --git a/src/.gitignore b/src/.gitignore
index 11fe5ab..60233c8 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,13 +1,12 @@
-genl-ctrl-dump
-genl-ctrl-get
-nf-ct-dump
+genl-ctrl-list
+nf-ct-list
 nf-log
 nf-monitor
 nl-addr-add
 nl-addr-delete
-nl-addr-dump
+nl-addr-list
 nl-fib-lookup
-nl-link-dump
+nl-link-list
 nl-link-ifindex2name
 nl-link-name2ifindex
 nl-link-set
@@ -17,15 +16,16 @@
 nl-monitor
 nl-neigh-add
 nl-neigh-delete
-nl-neigh-dump
-nl-neightbl-dump
+nl-neigh-list
+nl-neightbl-list
 nl-qdisc-add
 nl-qdisc-delete
-nl-qdisc-dump
+nl-qdisc-list
 nl-route-add
-nl-route-del
-nl-route-dump
+nl-route-delete
+nl-route-list
 nl-route-get
-nl-rule-dump
-nl-tctree-dump
+nl-rule-list
+nl-tctree-list
 nl-util-addr
+nf-queue
diff --git a/src/COPYING b/src/COPYING
new file mode 100644
index 0000000..4432540
--- /dev/null
+++ b/src/COPYING
@@ -0,0 +1,676 @@
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+ 
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+  
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644
index ddbe29a..0000000
--- a/src/Makefile
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# src/Makefile
-#
-# 	This library is free software; you can redistribute it and/or
-#	modify it under the terms of the GNU Lesser General Public
-#	License as published by the Free Software Foundation version 2.1
-#	of the License.
-#
-# Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
-#
-
-ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),)
-    include ../Makefile.opts
-endif
-
-LDFLAGS	+= -L../lib -lnl utils.o
-CIN 	:= $(wildcard nl-*.c) $(wildcard genl-*.c) $(wildcard nf-*.c)
-TOOLS	:= $(CIN:%.c=%)
-
-all: $(TOOLS)
-
-$(TOOLS): utils.o
-
-nl-%: nl-%.c
-	@echo "  LD $@"; \
-	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
-
-genl-%: genl-%.c
-	@echo "  LD $@"; \
-	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
-
-nf-%: nf-%.c
-	@echo "  LD $@"; \
-	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
-
-clean:
-	@echo "  CLEAN src"; \
-	rm -f $(TOOLS) utils.o
-
-distclean: clean
-
-install:
-	@true
-
-include ../Makefile.rules
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..dda32a7
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,101 @@
+# -*- Makefile -*-
+
+SUBDIRS = lib
+
+AM_CPPFLAGS  = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE
+AM_LDFLAGS = -L${top_builddir}/lib -L${top_builddir}/src/lib -lnl-cli
+
+noinst_PROGRAMS = \
+	genl-ctrl-list \
+	nf-ct-list nf-log nf-queue nf-monitor \
+	nl-addr-add nl-addr-delete nl-addr-list \
+	nl-link-list nl-link-set nl-link-stats \
+	nl-link-ifindex2name nl-link-name2ifindex \
+	nl-neigh-add nl-neigh-delete nl-neigh-list \
+	nl-qdisc-delete nl-qdisc-list \
+	nl-rule-list \
+	nl-neightbl-list \
+	nl-monitor \
+	nl-tctree-list \
+	nl-route-add nl-route-delete nl-route-get nl-route-list \
+	nl-fib-lookup \
+	nl-list-caches nl-list-sockets \
+	nl-util-addr \
+	nl-pktloc-lookup
+
+genl_ctrl_list_SOURCES = genl-ctrl-list.c 
+genl_ctrl_list_LDADD = -lnl-genl -lnl-route
+
+nf_ct_list_SOURCES = nf-ct-list.c 
+nf_ct_list_LDADD = -lnl-nf
+nf_log_SOURCES = nf-log.c
+nf_log_LDADD = -lnl-nf
+nf_queue_SOURCES = nf-queue.c 
+nf_queue_LDADD = -lnl-nf
+nf_monitor_SOURCES = nf-monitor.c
+nf_monitor_LDADD = -lnl-nf
+
+nl_addr_add_SOURCES = nl-addr-add.c
+nl_addr_add_LDADD = -lnl-route
+nl_addr_delete_SOURCES = nl-addr-delete.c
+nl_addr_delete_LDADD = -lnl-route
+nl_addr_list_SOURCES = nl-addr-list.c
+nl_addr_list_LDADD = -lnl-route
+
+nl_link_list_SOURCES = nl-link-list.c
+nl_link_list_LDADD = -lnl-route
+nl_link_set_SOURCES = nl-link-set.c
+nl_link_set_LDADD = -lnl-route
+nl_link_stats_SOURCES = nl-link-stats.c
+nl_link_stats_LDADD = -lnl-route
+nl_link_ifindex2name_SOURCES = nl-link-ifindex2name.c
+nl_link_ifindex2name_LDADD = -lnl-route
+nl_link_name2ifindex_SOURCES = nl-link-name2ifindex.c
+nl_link_name2ifindex_LDADD = -lnl-route
+
+nl_monitor_SOURCES = nl-monitor.c
+nl_monitor_LDADD = -lnl-route
+
+nl_neigh_add_SOURCES = nl-neigh-add.c
+nl_neigh_add_LDADD = -lnl-route
+nl_neigh_delete_SOURCES = nl-neigh-delete.c
+nl_neigh_delete_LDADD = -lnl-route
+nl_neigh_list_SOURCES = nl-neigh-list.c
+nl_neigh_list_LDADD = -lnl-route
+
+nl_neightbl_list_SOURCES = nl-neightbl-list.c
+nl_neightbl_list_LDADD = -lnl-route
+
+nl_qdisc_delete_SOURCES = nl-qdisc-delete.c
+nl_qdisc_delete_LDADD = -lnl-route
+nl_qdisc_list_SOURCES = nl-qdisc-list.c
+nl_qdisc_list_LDADD = -lnl-route
+
+nl_route_add_SOURCES = nl-route-add.c
+nl_route_add_LDADD = -lnl-route
+nl_route_delete_SOURCES = nl-route-delete.c
+nl_route_delete_LDADD = -lnl-route
+nl_route_get_SOURCES = nl-route-get.c
+nl_route_get_LDADD = -lnl-route
+nl_route_list_SOURCES = nl-route-list.c
+nl_route_list_LDADD = -lnl-route
+
+nl_rule_list_SOURCES = nl-rule-list.c
+nl_rule_list_LDADD = -lnl-route
+
+nl_tctree_list_SOURCES = nl-tctree-list.c
+nl_tctree_list_LDADD = -lnl-route
+
+nl_fib_lookup_SOURCES = nl-fib-lookup.c
+nl_fib_lookup_LDADD = -lnl-route
+
+nl_list_caches_SOURCES = nl-list-caches.c
+nl_list_caches_LDADD = -lnl-route
+nl_list_sockets_SOURCES = nl-list-sockets.c
+nl_list_sockets_LDADD = -lnl-route
+
+nl_util_addr_SOURCES = nl-util-addr.c
+nl_util_addr_LDADD = -lnl-route
+
+nl_pktloc_lookup_SOURCES = nl-pktloc-lookup.c
+nl_pktloc_lookup_LDADD = -lnl-route
diff --git a/src/cls/basic.c b/src/cls/basic.c
new file mode 100644
index 0000000..df1c112
--- /dev/null
+++ b/src/cls/basic.c
@@ -0,0 +1,90 @@
+/*
+ * src/cls/basic.c	Basic Classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation version 2 of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "utils.h"
+#include <netlink/route/cls/basic.h>
+#include <netlink/route/cls/ematch.h>
+
+static void print_usage(void)
+{
+	printf(
+"Usage: ... basic [OPTIONS]...\n"
+"\n"
+"Options\n"
+" -h, --help                Show this help.\n"
+" -e, --ematch=MATCH        Extended match (See --ematch help).\n"
+" -c, --classid=HANDLE      Target class to classify matching packets to.\n"
+	);
+	exit(0);
+}
+
+static void basic_parse_argv(struct rtnl_cls *cls, int argc, char **argv)
+{
+	uint32_t classid;
+
+	for (;;) {
+		int c, optidx = 0, err;
+		static struct option long_opts[] = {
+			{ "help", 0, 0, 'h' },
+			{ "ematch", 1, 0, 'e' },
+			{ "classid", 1, 0, 'c' },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "he:c:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case '?':
+			exit(NLE_INVAL);
+
+		case 'h':
+			print_usage();
+
+		case 'e':
+#if 0
+			if ((err = parse_ematch_syntax(optarg, &tree)) < 0)
+				fatal(err, "Error while parsing ematch: %s",
+				      nl_geterror(err));
+
+			if ((err = rtnl_basic_set_ematch(cls, tree)) < 0)
+				fatal(err, "Unable to set ematch: %s",
+					nl_geterror(err));
+#endif
+			break;
+
+		case 'c':
+			if ((err = rtnl_tc_str2handle(optarg, &classid)) < 0)
+				fatal(err, "Invalid classid \"%s\": %s",
+				      optarg, nl_geterror(err));
+				
+			if ((err = rtnl_basic_set_classid(cls, classid)) < 0)
+				fatal(err, "Unable to set classid: %s",
+				      nl_geterror(err));
+			break;
+		}
+ 	}
+}
+
+static struct cls_module basic_module = {
+	.name		= "basic",
+	.parse_argv	= basic_parse_argv,
+};
+
+static void __attribute__ ((constructor)) basic_init(void)
+{
+	register_cls_module(&basic_module);
+}
+
+static void __attribute__ ((destructor)) basic_exit(void)
+{
+	unregister_cls_module(&basic_module);
+}
diff --git a/src/cls/cgroup.c b/src/cls/cgroup.c
new file mode 100644
index 0000000..ad0392f
--- /dev/null
+++ b/src/cls/cgroup.c
@@ -0,0 +1,78 @@
+/*
+ * src/cls/cgroup.c	Control Groups Classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation version 2 of the License.
+ *
+ * Copyright (c) 2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "utils.h"
+#include <netlink/route/cls/cgroup.h>
+#include <netlink/route/cls/ematch.h>
+
+static void print_usage(void)
+{
+	printf(
+"Usage: ... cgroup [OPTIONS]...\n"
+"\n"
+"Options\n"
+" -h, --help                Show this help.\n"
+" -e, --ematch=MATCH        Extended match (See --ematch help).\n"
+" -c, --classid=HANDLE      Target class to classify matching packets to.\n"
+	);
+	exit(0);
+}
+
+static void basic_parse_argv(struct rtnl_cls *cls, int argc, char **argv)
+{
+	for (;;) {
+		int c, optidx = 0;
+		static struct option long_opts[] = {
+			{ "help", 0, 0, 'h' },
+			{ "ematch", 1, 0, 'e' },
+			{ "classid", 1, 0, 'c' },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "he:c:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case '?':
+			exit(NLE_INVAL);
+
+		case 'h':
+			print_usage();
+
+#if 0
+		case 'e':
+			if ((err = parse_ematch_syntax(optarg, &tree)) < 0)
+				fatal(err, "Error while parsing ematch: %s",
+				      nl_geterror(err));
+
+			if ((err = rtnl_basic_set_ematch(cls, tree)) < 0)
+				fatal(err, "Unable to set ematch: %s",
+					nl_geterror(err));
+			break;
+#endif
+		}
+ 	}
+}
+
+static struct cls_module cgroup_module = {
+	.name		= "cgroup",
+	.parse_argv	= basic_parse_argv,
+};
+
+static void __init cgroup_init(void)
+{
+	register_cls_module(&cgroup_module);
+}
+
+static void __exit cgroup_exit(void)
+{
+	unregister_cls_module(&cgroup_module);
+}
diff --git a/src/cls/utils.c b/src/cls/utils.c
new file mode 100644
index 0000000..ef6603b
--- /dev/null
+++ b/src/cls/utils.c
@@ -0,0 +1,105 @@
+/*
+ * src/cls-utils.c     Classifier Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation version 2 of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "utils.h"
+
+struct rtnl_cls *nlt_alloc_cls(void)
+{
+	struct rtnl_cls *cls;
+
+	cls = rtnl_cls_alloc();
+	if (!cls)
+		fatal(ENOMEM, "Unable to allocate classifier object");
+
+	return cls;
+}
+
+void parse_dev(struct rtnl_cls *cls, struct nl_cache *link_cache, char *arg)
+{
+	int ival;
+
+	if (!(ival = rtnl_link_name2i(link_cache, arg)))
+		fatal(ENOENT, "Link \"%s\" does not exist", arg);
+
+	rtnl_cls_set_ifindex(cls, ival);
+}
+  
+void parse_prio(struct rtnl_cls *cls, char *arg)
+{
+	uint32_t prio = parse_u32(arg);
+	rtnl_cls_set_prio(cls, prio);
+}
+
+void parse_parent(struct rtnl_cls *cls, char *arg)
+{
+	uint32_t parent;
+	int err;
+
+	if ((err = rtnl_tc_str2handle(arg, &parent)) < 0)
+		fatal(err, "Unable to parse handle \"%s\": %s",
+		      arg, nl_geterror(err));
+
+	rtnl_cls_set_parent(cls, parent);
+}
+
+void parse_handle(struct rtnl_cls *cls, char *arg)
+{
+	uint32_t handle;
+	int err;
+
+	if ((err = rtnl_tc_str2handle(arg, &handle)) < 0)
+		fatal(err, "Unable to parse handle \"%s\": %s",
+		      arg, nl_geterror(err));
+
+	rtnl_cls_set_handle(cls, handle);
+}
+
+void parse_proto(struct rtnl_cls *cls, char *arg)
+{
+	int proto = nl_str2ether_proto(arg);
+	if (proto < 0)
+		fatal(proto, "Unable to parse protocol \"%s\": %s",
+		      arg, nl_geterror(proto));
+	rtnl_cls_set_protocol(cls, proto);
+}
+
+static NL_LIST_HEAD(cls_modules);
+
+struct cls_module *lookup_cls_mod(struct rtnl_cls_ops *ops)
+{
+	struct cls_module *mod;
+
+	nl_list_for_each_entry(mod, &cls_modules, list) {
+		if (mod->ops == ops)
+			return mod;
+	}
+
+	return NULL;
+}
+
+void register_cls_module(struct cls_module *mod)
+{
+	struct rtnl_cls_ops *ops;
+
+	if (!(ops = __rtnl_cls_lookup_ops(mod->name)))
+		fatal(ENOENT, "Could not locate classifier module \"%s\"",
+			mod->name);
+
+	if (lookup_cls_mod(ops) != NULL)
+		fatal(EEXIST, "Duplicate classifier module registration.");
+
+	mod->ops = ops;
+	nl_list_add_tail(&mod->list, &cls_modules);
+}
+
+void unregister_cls_module(struct cls_module *mod)
+{
+	nl_list_del(&mod->list);
+}
diff --git a/src/cls/utils.h b/src/cls/utils.h
new file mode 100644
index 0000000..1a8ee9b
--- /dev/null
+++ b/src/cls/utils.h
@@ -0,0 +1,51 @@
+/*
+ * src/cls-utils.h     Classifier Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation version 2 of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __CLS_UTILS_H_
+#define __CLS_UTILS_H_
+
+#include "../utils.h"
+#include <netlink/route/classifier-modules.h>
+#include <netlink/route/cls/ematch.h>
+
+struct cls_module
+{
+	const char *		name;
+	struct rtnl_cls_ops *	ops;
+	void		      (*parse_argv)(struct rtnl_cls *, int, char **);
+	struct nl_list_head	list;
+};
+
+extern struct cls_module *lookup_cls_mod(struct rtnl_cls_ops *);
+extern void register_cls_module(struct cls_module *);
+extern void unregister_cls_module(struct cls_module *);
+
+struct ematch_module
+{
+	int kind;
+	struct rtnl_ematch_ops *ops;
+	void (*parse_argv)(struct rtnl_ematch *, int, char **);
+	struct nl_list_head list;
+};
+
+extern struct ematch_module *lookup_ematch_mod(struct rtnl_ematch_ops *);
+extern void register_ematch_module(struct ematch_module *);
+extern void unregister_ematch_module(struct ematch_module *);
+
+extern struct rtnl_cls *nlt_alloc_cls(void);
+extern void parse_dev(struct rtnl_cls *, struct nl_cache *, char *);
+extern void parse_prio(struct rtnl_cls *, char *);
+extern void parse_parent(struct rtnl_cls *, char *);
+extern void parse_handle(struct rtnl_cls *, char *);
+extern void parse_proto(struct rtnl_cls *, char *);
+
+extern int parse_ematch_syntax(const char *, struct rtnl_ematch_tree **);
+
+#endif
diff --git a/src/nl-qdisc-add.c b/src/disabled-nl-qdisc-add.c
similarity index 99%
rename from src/nl-qdisc-add.c
rename to src/disabled-nl-qdisc-add.c
index be19f9b..a1fab4e 100644
--- a/src/nl-qdisc-add.c
+++ b/src/disabled-nl-qdisc-add.c
@@ -125,7 +125,7 @@
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
+	struct nl_sock *nlh;
 	struct rtnl_qdisc *qdisc;
 	uint32_t handle, parent;
 	int err = 1;
diff --git a/src/f_addr.c b/src/f_addr.c
deleted file mode 100644
index 491d88f..0000000
--- a/src/f_addr.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * src/f_addr.c     	Address Filter
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-static void get_filter(struct rtnl_addr *addr, int argc, char **argv, int idx,
-		       struct nl_cache *link_cache)
-{
-	struct nl_addr *a;
-
-	while (argc > idx) {
-		if (arg_match("dev")) {
-			if (argc > ++idx) {
-				int ifindex = rtnl_link_name2i(link_cache, argv[idx++]);
-				if (ifindex == RTNL_LINK_NOT_FOUND)
-					goto err_notfound;
-				rtnl_addr_set_ifindex(addr, ifindex);
-			}
-		} else if (arg_match("family")) {
-			if (argc > ++idx) {
-				int family = nl_str2af(argv[idx++]);
-				if (family == AF_UNSPEC)
-					goto err_invaf;
-				rtnl_addr_set_family(addr, family);
-			}
-		} else if (arg_match("label")) {
-			if (argc > ++idx)
-				rtnl_addr_set_label(addr, argv[idx++]);
-		} else if (arg_match("scope")) {
-			if (argc > ++idx) {
-				int scope = rtnl_str2scope(argv[idx++]);
-				if (scope < 0)
-					goto err_invscope;
-				rtnl_addr_set_scope(addr, scope);
-			}
-		} else if (arg_match("local")) {
-			if (argc > ++idx) {
-				a = nl_addr_parse(argv[idx++],
-						  rtnl_addr_get_family(addr));
-				if (!a)
-					goto err_invaddr;
-				rtnl_addr_set_local(addr, a);
-				nl_addr_put(a);
-			}
-		} else if (arg_match("peer")) {
-			if (argc > ++idx) {
-				a = nl_addr_parse(argv[idx++],
-						  rtnl_addr_get_family(addr));
-				if (!a)
-					goto err_invaddr;
-				rtnl_addr_set_peer(addr, a);
-				nl_addr_put(a);
-			}
-		} else if (arg_match("broadcast")) {
-			if (argc > ++idx) {
-				a = nl_addr_parse(argv[idx++],
-						  rtnl_addr_get_family(addr));
-				if (!a)
-					goto err_invaddr;
-				rtnl_addr_set_broadcast(addr, a);
-				nl_addr_put(a);
-			}
-		} else if (arg_match("multicast")) {
-			if (argc > ++idx) {
-				a = nl_addr_parse(argv[idx++],
-						  rtnl_addr_get_family(addr));
-				if (!a)
-					goto err_invaddr;
-				rtnl_addr_set_multicast(addr, a);
-				nl_addr_put(a);
-			}
-		} else if (arg_match("anycast")) {
-			if (argc > ++idx) {
-				a = nl_addr_parse(argv[idx++],
-						  rtnl_addr_get_family(addr));
-				if (!a)
-					goto err_invaddr;
-				rtnl_addr_set_anycast(addr, a);
-				nl_addr_put(a);
-			}
-		} else {
-			fprintf(stderr, "What is '%s'?\n", argv[idx]);
-			exit(1);
-		}
-	}
-
-	return;
-
-err_notfound:
-	fprintf(stderr, "Unknown link %s\n", argv[idx-1]);
-	exit(1);
-err_invscope:
-	fprintf(stderr, "Invalid scope name \"%s\".\n", argv[idx-1]);
-	exit(1);
-err_invaf:
-	fprintf(stderr, "Invalid address family \"%s\"\n", argv[idx-1]);
-	exit(1);
-err_invaddr:
-	fprintf(stderr, "Invalid address \"%s\": %s\n", argv[idx-1], nl_geterror());
-	exit(1);
-}
diff --git a/src/f_ct.c b/src/f_ct.c
deleted file mode 100644
index fbb04fc..0000000
--- a/src/f_ct.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * src/f_ct.c     	Conntrack Filter
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
- * Copyright (c) 2007 Secure Computing Corporation
- */
-
-static void get_filter(struct nfnl_ct *ct, int argc, char **argv, int idx)
-{
-	struct nl_addr *a;
-
-	while (argc > idx) {
-		if (arg_match("family")) {
-			if (argc > ++idx) {
-				int family = nl_str2af(argv[idx++]);
-				if (family == AF_UNSPEC)
-					goto err_invaf;
-				nfnl_ct_set_family(ct, family);
-			}
-		} else if (arg_match("proto")) {
-			if (argc > ++idx) {
-				int proto = nl_str2ip_proto(argv[idx++]);
-				if (proto < 0)
-					goto err_invproto;
-				nfnl_ct_set_proto(ct, proto);
-			}
-		} else if (arg_match("tcpstate")) {
-			if (argc > ++idx) {
-				int state = nfnl_ct_str2tcp_state(argv[idx++]);
-				if (state < 0)
-					goto err_invtcpstate;
-				nfnl_ct_set_tcp_state(ct, state);
-			}
-		} else if (arg_match("status")) {
-			if (argc > ++idx) {
-				int status = strtoul(argv[idx++], NULL, 0);
-				nfnl_ct_set_status(ct, status);
-				nfnl_ct_unset_status(ct, ~status);
-			}
-		} else if (arg_match("timeout")) {
-			if (argc > ++idx)
-				nfnl_ct_set_timeout(ct, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("mark")) {
-			if (argc > ++idx)
-				nfnl_ct_set_mark(ct, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("use")) {
-			if (argc > ++idx)
-				nfnl_ct_set_use(ct, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("id")) {
-			if (argc > ++idx)
-				nfnl_ct_set_id(ct, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("origsrc")) {
-			if (argc > ++idx) {
-				a = nl_addr_parse(argv[idx++],
-						  nfnl_ct_get_family(ct));
-				if (!a)
-					goto err_invaddr;
-				nfnl_ct_set_src(ct, 0, a);
-				nl_addr_put(a);
-			}
-		} else if (arg_match("origdst")) {
-			if (argc > ++idx) {
-				a = nl_addr_parse(argv[idx++],
-						  nfnl_ct_get_family(ct));
-				if (!a)
-					goto err_invaddr;
-				nfnl_ct_set_dst(ct, 0, a);
-				nl_addr_put(a);
-			}
-		} else if (arg_match("origsrcport")) {
-			if (argc > ++idx)
-				nfnl_ct_set_src_port(ct, 0, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("origdstport")) {
-			if (argc > ++idx)
-				nfnl_ct_set_dst_port(ct, 0, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("origicmpid")) {
-			if (argc > ++idx)
-				nfnl_ct_set_icmp_id(ct, 0, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("origicmptype")) {
-			if (argc > ++idx)
-				nfnl_ct_set_icmp_type(ct, 0, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("origicmpcode")) {
-			if (argc > ++idx)
-				nfnl_ct_set_icmp_code(ct, 0, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("origpackets")) {
-			if (argc > ++idx)
-				nfnl_ct_set_packets(ct, 0, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("origbytes")) {
-			if (argc > ++idx)
-				nfnl_ct_set_bytes(ct, 0, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("replysrc")) {
-			if (argc > ++idx) {
-				a = nl_addr_parse(argv[idx++],
-						  nfnl_ct_get_family(ct));
-				if (!a)
-					goto err_invaddr;
-				nfnl_ct_set_src(ct, 1, a);
-				nl_addr_put(a);
-			}
-		} else if (arg_match("replydst")) {
-			if (argc > ++idx) {
-				a = nl_addr_parse(argv[idx++],
-						  nfnl_ct_get_family(ct));
-				if (!a)
-					goto err_invaddr;
-				nfnl_ct_set_dst(ct, 1, a);
-				nl_addr_put(a);
-			}
-		} else if (arg_match("replysrcport")) {
-			if (argc > ++idx)
-				nfnl_ct_set_src_port(ct, 1, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("replydstport")) {
-			if (argc > ++idx)
-				nfnl_ct_set_dst_port(ct, 1, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("replyicmpid")) {
-			if (argc > ++idx)
-				nfnl_ct_set_icmp_id(ct, 1, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("replyicmptype")) {
-			if (argc > ++idx)
-				nfnl_ct_set_icmp_type(ct, 1, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("replyicmpcode")) {
-			if (argc > ++idx)
-				nfnl_ct_set_icmp_code(ct, 1, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("replypackets")) {
-			if (argc > ++idx)
-				nfnl_ct_set_packets(ct, 1, strtoul(argv[idx++], NULL, 0));
-		} else if (arg_match("replybytes")) {
-			if (argc > ++idx)
-				nfnl_ct_set_bytes(ct, 1, strtoul(argv[idx++], NULL, 0));
-		}
-#define MSTATUS(STR, STATUS) \
-	else if (!strcasecmp(argv[idx], STR)) { \
-		nfnl_ct_set_status(ct, STATUS); idx++; }
-#define MNOSTATUS(STR, STATUS) \
-	else if (!strcasecmp(argv[idx], STR)) { \
-		nfnl_ct_unset_status(ct, STATUS); idx++; }
-
-		MSTATUS("replied", IPS_SEEN_REPLY)
-		MNOSTATUS("unreplied", IPS_SEEN_REPLY)
-		MSTATUS("assured", IPS_ASSURED)
-		MNOSTATUS("unassured", IPS_ASSURED)
-#undef MSTATUS
-#undef MNOSTATUS
-		else {
-			fprintf(stderr, "What is '%s'?\n", argv[idx]);
-			exit(1);
-		}
-	}
-
-	return;
-
-err_invproto:
-	fprintf(stderr, "Invalid IP protocol \"%s\".\n", argv[idx-1]);
-	exit(1);
-err_invtcpstate:
-	fprintf(stderr, "Invalid TCP state \"%s\".\n", argv[idx-1]);
-	exit(1);
-err_invaf:
-	fprintf(stderr, "Invalid address family \"%s\"\n", argv[idx-1]);
-	exit(1);
-err_invaddr:
-	fprintf(stderr, "Invalid address \"%s\": %s\n", argv[idx-1], nl_geterror());
-	exit(1);
-}
diff --git a/src/f_link.c b/src/f_link.c
deleted file mode 100644
index 3c1cb93..0000000
--- a/src/f_link.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * src/f_link.c		Link Filter
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include <linux/if.h>
-
-static void get_filter(struct rtnl_link *l, int ac, char **av, int idx,
-		       struct nl_cache *cache)
-{
-	while (ac > idx) {
-		if (!strcasecmp(av[idx], "dev")) {
-			if (ac > ++idx) {
-				int ifindex = rtnl_link_name2i(cache, av[idx++]);
-				if (ifindex == RTNL_LINK_NOT_FOUND)
-					goto err_notfound;
-				rtnl_link_set_ifindex(l, ifindex);
-			}
-		} else if (!strcasecmp(av[idx], "mtu")) {
-			if (ac > ++idx)
-				rtnl_link_set_mtu(l, strtoul(av[idx++], NULL, 0));
-		} else if (!strcasecmp(av[idx], "txqlen")) {
-			if (ac > ++idx)
-				rtnl_link_set_txqlen(l, strtoul(av[idx++], NULL, 0));
-		} else if (!strcasecmp(av[idx], "weight")) {
-			if (ac > ++idx)
-				rtnl_link_set_weight(l, strtoul(av[idx++], NULL, 0));
-		} else if (!strcasecmp(av[idx], "link")) {
-			if (ac > ++idx) {
-				int ifindex = rtnl_link_name2i(cache, av[idx++]);
-				if (ifindex == RTNL_LINK_NOT_FOUND)
-					goto err_notfound;
-				rtnl_link_set_link(l, ifindex);
-			}
-		} else if (!strcasecmp(av[idx], "master")) {
-			if (ac > ++idx) {
-				int ifindex = rtnl_link_name2i(cache, av[idx++]);
-				if (ifindex == RTNL_LINK_NOT_FOUND)
-					goto err_notfound;
-				rtnl_link_set_master(l, ifindex);
-			}
-		} else if (!strcasecmp(av[idx], "qdisc")) {
-			if (ac > ++idx)
-				rtnl_link_set_qdisc(l, av[idx++]);
-		} else if (!strcasecmp(av[idx], "name")) {
-			if (ac > ++idx)
-				rtnl_link_set_name(l, av[idx++]);
-		} else if (!strcasecmp(av[idx], "addr")) {
-			if (ac > ++idx) {
-				struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-				if (a == NULL)
-					goto err;
-				rtnl_link_set_addr(l, a);
-				nl_addr_put(a);
-			}
-		} else if (!strcasecmp(av[idx], "broadcast")) {
-			if (ac > ++idx) {
-				struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-				if (a == NULL)
-					goto err;
-				rtnl_link_set_broadcast(l, a);
-				nl_addr_put(a);
-			}
-		}
-#define MFLAG(STR, FLAG) \
-	else if (!strcasecmp(av[idx], STR)) { \
-		rtnl_link_set_flags(l, FLAG); idx++; }
-#define MNOFLAG(STR, FLAG) \
-	else if (!strcasecmp(av[idx], STR)) { \
-		rtnl_link_unset_flags(l, FLAG); idx++; }
-
-		MFLAG("up", IFF_UP)
-		MNOFLAG("down", IFF_UP)
-		MFLAG("noarp", IFF_NOARP)
-		MNOFLAG("arp", IFF_NOARP)
-		MFLAG("promisc", IFF_PROMISC)
-		MNOFLAG("nopromisc", IFF_PROMISC)
-		MFLAG("dynamic", IFF_DYNAMIC)
-		MNOFLAG("nodynamic", IFF_DYNAMIC)
-		MFLAG("multicast", IFF_MULTICAST)
-		MNOFLAG("nomulticast", IFF_MULTICAST)
-		MFLAG("allmulticast", IFF_ALLMULTI)
-		MNOFLAG("noallmulticast", IFF_ALLMULTI)
-#undef MFLAG
-#undef MNOFLAG
-		else {
-			fprintf(stderr, "What is '%s'?\n", av[idx]);
-			exit(1);
-		}
-	}
-
-	return;
-
-err_notfound:
-	fprintf(stderr, "Unknown link %s\n", av[idx-1]);
-	exit(1);
-err:
-	fprintf(stderr, "%s\n", nl_geterror());
-	exit(1);
-}
diff --git a/src/f_neigh.c b/src/f_neigh.c
deleted file mode 100644
index ac9355c..0000000
--- a/src/f_neigh.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * src/f_neigh.c	Neighbour Filter
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-static void get_filter(struct rtnl_neigh *n, int ac, char **av, int idx,
-		       struct nl_cache *cache)
-{
-	struct nl_cache *lc = nl_cache_mngt_require("route/link");
-	
-	while (ac > idx) {
-		if (!strcasecmp(av[idx], "dev")) {
-			if (ac > ++idx) {
-				int ifindex = rtnl_link_name2i(lc, av[idx++]);
-				if (ifindex == RTNL_LINK_NOT_FOUND)
-					goto err_notfound;
-				rtnl_neigh_set_ifindex(n, ifindex);
-			}
-		} else if (!strcasecmp(av[idx], "dst")) {
-			if (ac > ++idx) {
-				struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-				if (a == NULL)
-					goto err;
-				rtnl_neigh_set_dst(n, a);
-				nl_addr_put(a);
-			}
-		} else if (!strcasecmp(av[idx], "lladdr")) {
-			if (ac > ++idx) {
-				struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-				if (a == NULL)
-					goto err;
-				rtnl_neigh_set_lladdr(n, a);
-				nl_addr_put(a);
-			}
-		}
-	}
-
-	return;
-err_notfound:
-	fprintf(stderr, "Unable to find interface %s\n", av[idx-1]);
-	exit(1);
-err:
-	fprintf(stderr, "%s\n", nl_geterror());
-	exit(1);
-}
diff --git a/src/f_route.c b/src/f_route.c
deleted file mode 100644
index 581ff65..0000000
--- a/src/f_route.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * src/f_route.c	Routes Filter
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-static void get_filter(struct rtnl_route *r, int ac, char **av, int idx,
-		       struct nl_cache *cache, struct nl_cache *link_cache)
-{
-	while (ac > idx) {
-		if (!strcasecmp(av[idx], "src")) {
-			if (ac > ++idx) {
-				struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-				if (!a)
-					goto err;
-				rtnl_route_set_pref_src(r, a);
-				nl_addr_put(a);
-			}
-		} else if (!strcasecmp(av[idx], "dst")) {
-			if (ac > ++idx) {
-				struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-				if (!a)
-					goto err;
-				rtnl_route_set_dst(r, a);
-				nl_addr_put(a);
-			}
-		} else if (!strcasecmp(av[idx], "via")) {
-			if (ac > ++idx) {
-				struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-				if (!a)
-					goto err;
-				rtnl_route_set_gateway(r, a);
-				nl_addr_put(a);
-			}
-		} else if (!strcasecmp(av[idx], "from")) {
-			if (ac > ++idx) {
-				struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC);
-				if (!a)
-					goto err;
-				rtnl_route_set_src(r, a);
-				nl_addr_put(a);
-			}
-		} else if (!strcasecmp(av[idx], "tos")) {
-			if (ac > ++idx)
-				rtnl_route_set_tos(r, strtoul(av[idx++], NULL, 0));
-		} else if (!strcasecmp(av[idx], "prio")) {
-			if (ac > ++idx)
-				rtnl_route_set_prio(r, strtoul(av[idx++], NULL, 0));
-		} else if (!strcasecmp(av[idx], "scope")) {
-			if (ac > ++idx)
-				rtnl_route_set_scope(r, rtnl_str2scope(av[idx++]));
-		} else if (!strcasecmp(av[idx], "dev")) {
-			if (ac > ++idx) {
-				int ifindex = rtnl_link_name2i(link_cache, av[idx++]);
-				if (ifindex == RTNL_LINK_NOT_FOUND)
-					goto err_notfound;
-				rtnl_route_set_oif(r, ifindex);
-			}
-		} else if (!strcasecmp(av[idx], "table")) {
-			if (ac > ++idx)
-				rtnl_route_set_table(r, strtoul(av[idx++], NULL, 0));
-		} else {
-			fprintf(stderr, "What is '%s'?\n", av[idx]);
-			exit(1);
-		}
-	}
-
-	return;
-
-err_notfound:
-	fprintf(stderr, "Unable to find device \"%s\"\n", av[idx-1]);
-	exit(1);
-err:
-	fprintf(stderr, "%s\n", nl_geterror());
-	exit(1);
-}
diff --git a/src/genl-ctrl-dump.c b/src/genl-ctrl-dump.c
deleted file mode 100644
index 98a26a1..0000000
--- a/src/genl-ctrl-dump.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * src/genl-ctrl-dump.c	Dump Generic Netlink Controller
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-	printf(
-	"Usage: genl-ctrl-dump <mode> [<filter>]\n"
-	"  mode := { brief | detailed | stats }\n"
-	"  filter := \n");
-	exit(1);
-}
-
-int main(int argc, char *argv[])
-{
-	struct nl_handle *nlh;
-	struct nl_cache *family_cache;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF
-	};
-	int err = 1;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
-
-	if (genl_connect(nlh) < 0) {
-		fprintf(stderr, "Unable to connect generic netlink socket%s\n",
-			nl_geterror());
-		goto errout;
-	}
-
-	family_cache = nltool_alloc_genl_family_cache(nlh);
-	if (!family_cache)
-		goto errout;
-
-	params.dp_type = nltool_parse_dumptype(argv[1]);
-	if (params.dp_type < 0)
-		goto errout;
-
-	//get_filter(link, argc, argv, 2, link_cache);
-	nl_cache_dump(family_cache, &params);
-	nl_cache_free(family_cache);
-	err = 0;
-errout:
-	nl_close(nlh);
-	nl_handle_destroy(nlh);
-	return err;
-}
diff --git a/src/genl-ctrl-list.c b/src/genl-ctrl-list.c
new file mode 100644
index 0000000..e25eb2a
--- /dev/null
+++ b/src/genl-ctrl-list.c
@@ -0,0 +1,69 @@
+/*
+ * src/genl-ctrl-list.c	List Generic Netlink Controller
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+
+static struct nl_cache *alloc_genl_family_cache(struct nl_sock *sk)
+{
+	return nl_cli_alloc_cache(sk, "generic netlink family",
+			   genl_ctrl_alloc_cache);
+}
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: genl-ctrl-list [OPTION]...\n"
+	"\n"
+	"Options\n"
+	" -f, --format=TYPE     Output format { brief | details | stats }\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	);
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct nl_cache *family_cache;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+ 
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_GENERIC);
+	family_cache = alloc_genl_family_cache(sock);
+ 
+	for (;;) {
+		int c, optidx = 0;
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "f:hv", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		}
+ 	}
+
+	nl_cache_dump(family_cache, &params);
+
+	return 0;
+}
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
new file mode 100644
index 0000000..80c217c
--- /dev/null
+++ b/src/lib/Makefile.am
@@ -0,0 +1,41 @@
+# -*- Makefile -*-
+
+AM_CPPFLAGS  = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DPKGLIBDIR=\"$(pkglibdir)\" -DSYSCONFDIR=\"$(sysconfdir)\" -rdynamic
+AM_LDFLAGS = -L${top_builddir}/lib -ldl
+
+#nobase_pkglib_LTLIBRARIES = cls/basic.la cls/ematch/cmp.la
+#cls_basic_la_LDFLAGS = -module -version-info 2:0:0
+#cls_ematch_cmp_la_LDFLAGS = -module -version-info 2:0:0
+
+#cls/ematch_grammar.c: cls/ematch_grammar.l
+#	$(LEX) --header-file=cls/ematch_grammar.h $(LFLAGS) -o $@ $^
+
+#cls/ematch_syntax.c: cls/ematch_syntax.y
+#	$(YACC) -d $(YFLAGS) -o $@ $^
+
+#cls/pktloc_grammar.c: cls/pktloc_grammar.l
+#	$(LEX) --header-file=cls/pktloc_grammar.h $(LFLAGS) -o $@ $^
+
+#cls/pktloc_syntax.c: cls/pktloc_syntax.y
+#	$(YACC) -d $(YFLAGS) -o $@ $^
+
+#CLEANFILES = \
+#	cls/ematch_grammar.c cls/ematch_grammar.h \
+#	cls/ematch_syntax.c cls/ematch_syntax.h \
+#	cls/pktloc_grammar.c cls/pktloc_grammar.h \
+#	cls/pktloc_syntax.c cls/pktloc_syntax.h
+
+lib_LTLIBRARIES = \
+	libnl-cli.la
+
+libnl_cli_la_LDFLAGS = -version-info 2:0:0
+
+libnl_cli_la_LIBADD  = ${top_builddir}/lib/libnl.la \
+		       ${top_builddir}/lib/libnl-route.la \
+		       ${top_builddir}/lib/libnl-nf.la \
+		       ${top_builddir}/lib/libnl-genl.la
+
+libnl_cli_la_SOURCES = \
+	utils.c addr.c ct.c link.c neigh.c qdisc.c rule.c route.c
+#	cls/ematch_syntax.c cls/ematch_grammar.c cls/ematch.c
+#	cls/pktloc_syntax.c cls/pktloc_grammar.c cls/utils.c
diff --git a/src/lib/addr.c b/src/lib/addr.c
new file mode 100644
index 0000000..a9c137b
--- /dev/null
+++ b/src/lib/addr.c
@@ -0,0 +1,135 @@
+/*
+ * src/lib/addr.c     Address Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_addr Addresses
+ *
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/addr.h>
+
+struct rtnl_addr *nl_cli_addr_alloc(void)
+{
+	struct rtnl_addr *addr;
+
+	addr = rtnl_addr_alloc();
+	if (!addr)
+		nl_cli_fatal(ENOMEM, "Unable to allocate address object");
+
+	return addr;
+}
+
+void nl_cli_addr_parse_family(struct rtnl_addr *addr, char *arg)
+{
+	int family;
+
+	if ((family = nl_str2af(arg)) != AF_UNSPEC)
+		rtnl_addr_set_family(addr, family);
+}
+
+void nl_cli_addr_parse_local(struct rtnl_addr *addr, char *arg)
+{
+	struct nl_addr *a;
+	int err;
+
+	a = nl_cli_addr_parse(arg, rtnl_addr_get_family(addr));
+	if ((err = rtnl_addr_set_local(addr, a)) < 0)
+		nl_cli_fatal(err, "Unable to set local address: %s",
+			     nl_geterror(err));
+
+	nl_addr_put(a);
+}
+
+void nl_cli_addr_parse_dev(struct rtnl_addr *addr, struct nl_cache *link_cache,
+			   char *arg)
+{
+	int ival;
+
+	if (!(ival = rtnl_link_name2i(link_cache, arg)))
+		nl_cli_fatal(ENOENT, "Link \"%s\" does not exist", arg);
+
+	rtnl_addr_set_ifindex(addr, ival);
+}
+
+void nl_cli_addr_parse_label(struct rtnl_addr *addr, char *arg)
+{
+	int err;
+
+	if ((err = rtnl_addr_set_label(addr, arg)) < 0)
+		nl_cli_fatal(err, "Unable to set address label: %s",
+			     nl_geterror(err));
+}
+
+void nl_cli_addr_parse_peer(struct rtnl_addr *addr, char *arg)
+{
+	struct nl_addr *a;
+	int err;
+
+	a = nl_cli_addr_parse(arg, rtnl_addr_get_family(addr));
+	if ((err = rtnl_addr_set_peer(addr, a)) < 0)
+		nl_cli_fatal(err, "Unable to set peer address: %s",
+			     nl_geterror(err));
+
+	nl_addr_put(a);
+}
+
+void nl_cli_addr_parse_scope(struct rtnl_addr *addr, char *arg)
+{
+	int ival;
+
+	if ((ival = rtnl_str2scope(arg)) < 0)
+		nl_cli_fatal(EINVAL, "Unknown address scope \"%s\"", arg);
+
+	rtnl_addr_set_scope(addr, ival);
+}
+
+void nl_cli_addr_parse_broadcast(struct rtnl_addr *addr, char *arg)
+{
+	struct nl_addr *a;
+	int err;
+
+	a = nl_cli_addr_parse(arg, rtnl_addr_get_family(addr));
+	if ((err = rtnl_addr_set_broadcast(addr, a)) < 0)
+		nl_cli_fatal(err, "Unable to set broadcast address: %s",
+			     nl_geterror(err));
+
+	nl_addr_put(a);
+}
+
+static uint32_t parse_lifetime(const char *arg)
+{
+	uint64_t msecs;
+	int err;
+
+	if (!strcasecmp(arg, "forever"))
+		return 0xFFFFFFFFU;
+
+	if ((err = nl_str2msec(arg, &msecs)) < 0)
+		nl_cli_fatal(err, "Unable to parse time string \"%s\": %s",
+			     arg, nl_geterror(err));
+
+	return (msecs / 1000);
+}
+
+void nl_cli_addr_parse_preferred(struct rtnl_addr *addr, char *arg)
+{
+	rtnl_addr_set_preferred_lifetime(addr, parse_lifetime(arg));
+}
+
+void nl_cli_addr_parse_valid(struct rtnl_addr *addr, char *arg)
+{
+	rtnl_addr_set_valid_lifetime(addr, parse_lifetime(arg));
+}
+
+/** @} */
diff --git a/src/lib/ct.c b/src/lib/ct.c
new file mode 100644
index 0000000..5bab08f
--- /dev/null
+++ b/src/lib/ct.c
@@ -0,0 +1,162 @@
+/*
+ * src/lib/ct.c		CLI Conntrack Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_ct Connection Tracking
+ *
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/ct.h>
+
+struct nfnl_ct *nl_cli_ct_alloc(void)
+{
+	struct nfnl_ct *ct;
+
+	ct = nfnl_ct_alloc();
+	if (!ct)
+		nl_cli_fatal(ENOMEM, "Unable to allocate conntrack object");
+
+	return ct;
+}
+
+struct nl_cache *nl_cli_ct_alloc_cache(struct nl_sock *sk)
+{
+	return nl_cli_alloc_cache(sk, "conntrack", nfnl_ct_alloc_cache);
+}
+
+void nl_cli_ct_parse_family(struct nfnl_ct *ct, char *arg)
+{
+	int family;
+
+	if ((family = nl_str2af(arg)) == AF_UNSPEC)
+		nl_cli_fatal(EINVAL,
+			     "Unable to nl_cli_ct_parse family \"%s\": %s",
+			     arg, nl_geterror(NLE_INVAL));
+
+	nfnl_ct_set_family(ct, family);
+}
+
+void nl_cli_ct_parse_protocol(struct nfnl_ct *ct, char *arg)
+{
+	int proto;
+
+	if ((proto = nl_str2ip_proto(arg)) < 0)
+		nl_cli_fatal(proto,
+			     "Unable to nl_cli_ct_parse protocol \"%s\": %s",
+			     arg, nl_geterror(proto));
+
+	nfnl_ct_set_proto(ct, proto);
+}
+
+void nl_cli_ct_parse_mark(struct nfnl_ct *ct, char *arg)
+{
+	uint32_t mark = nl_cli_parse_u32(arg);
+	nfnl_ct_set_mark(ct, mark);
+}
+
+void nl_cli_ct_parse_timeout(struct nfnl_ct *ct, char *arg)
+{
+	uint32_t timeout = nl_cli_parse_u32(arg);
+	nfnl_ct_set_timeout(ct, timeout);
+}
+
+void nl_cli_ct_parse_id(struct nfnl_ct *ct, char *arg)
+{
+	uint32_t id = nl_cli_parse_u32(arg);
+	nfnl_ct_set_id(ct, id);
+}
+
+void nl_cli_ct_parse_use(struct nfnl_ct *ct, char *arg)
+{
+	uint32_t use = nl_cli_parse_u32(arg);
+	nfnl_ct_set_use(ct, use);
+}
+
+void nl_cli_ct_parse_src(struct nfnl_ct *ct, int reply, char *arg)
+{
+	int err;
+	struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_ct_get_family(ct));
+	if ((err = nfnl_ct_set_src(ct, reply, a)) < 0)
+		nl_cli_fatal(err, "Unable to set source address: %s",
+			     nl_geterror(err));
+}
+
+void nl_cli_ct_parse_dst(struct nfnl_ct *ct, int reply, char *arg)
+{
+	int err;
+	struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_ct_get_family(ct));
+	if ((err = nfnl_ct_set_dst(ct, reply, a)) < 0)
+		nl_cli_fatal(err, "Unable to set destination address: %s",
+			     nl_geterror(err));
+}
+
+void nl_cli_ct_parse_src_port(struct nfnl_ct *ct, int reply, char *arg)
+{
+	uint32_t port = nl_cli_parse_u32(arg);
+	nfnl_ct_set_src_port(ct, reply, port);
+}
+
+void nl_cli_ct_parse_dst_port(struct nfnl_ct *ct, int reply, char *arg)
+{
+	uint32_t port = nl_cli_parse_u32(arg);
+	nfnl_ct_set_dst_port(ct, reply, port);
+}
+
+void nl_cli_ct_parse_tcp_state(struct nfnl_ct *ct, char *arg)
+{
+	int state;
+
+	if ((state = nfnl_ct_str2tcp_state(arg)) < 0)
+		nl_cli_fatal(state,
+			     "Unable to nl_cli_ct_parse tcp state \"%s\": %s",
+			     arg, nl_geterror(state));
+
+	nfnl_ct_set_tcp_state(ct, state);
+}
+
+void nl_cli_ct_parse_status(struct nfnl_ct *ct, char *arg)
+{
+	int status;
+
+	if ((status = nfnl_ct_str2status(arg)) < 0)
+		nl_cli_fatal(status,
+			     "Unable to nl_cli_ct_parse flags \"%s\": %s",
+			     arg, nl_geterror(status));
+
+	nfnl_ct_set_status(ct, status);
+}
+
+#if 0
+		} else if (arg_match("origicmpid")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_id(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("origicmptype")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_type(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("origicmpcode")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_code(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replyicmpid")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_id(ct, 1, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replyicmptype")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_type(ct, 1, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replyicmpcode")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_code(ct, 1, strtoul(argv[idx++], NULL, 0));
+		}
+#endif
+
+/** @} */
diff --git a/src/lib/link.c b/src/lib/link.c
new file mode 100644
index 0000000..c192569
--- /dev/null
+++ b/src/lib/link.c
@@ -0,0 +1,73 @@
+/*
+ * src/lib/link.c     CLI Link Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_link Links
+ *
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
+
+struct rtnl_link *nl_cli_link_alloc(void)
+{
+	struct rtnl_link *link;
+
+	link = rtnl_link_alloc();
+	if (!link)
+		nl_cli_fatal(ENOMEM, "Unable to allocate link object");
+
+	return link;
+}
+
+void nl_cli_link_parse_family(struct rtnl_link *link, char *arg)
+{
+	int family;
+
+	if ((family = nl_str2af(arg)) == AF_UNSPEC)
+		nl_cli_fatal(EINVAL,
+			     "Unable to translate address family \"%s\"", arg);
+
+	rtnl_link_set_family(link, family);
+}
+
+void nl_cli_link_parse_name(struct rtnl_link *link, char *arg)
+{
+	rtnl_link_set_name(link, arg);
+}
+
+void nl_cli_link_parse_mtu(struct rtnl_link *link, char *arg)
+{
+	uint32_t mtu = nl_cli_parse_u32(arg);
+	rtnl_link_set_mtu(link, mtu);
+}
+
+void nl_cli_link_parse_ifindex(struct rtnl_link *link, char *arg)
+{
+	uint32_t index = nl_cli_parse_u32(arg);
+	rtnl_link_set_ifindex(link, index);
+}
+
+void nl_cli_link_parse_txqlen(struct rtnl_link *link, char *arg)
+{
+	uint32_t qlen = nl_cli_parse_u32(arg);
+	rtnl_link_set_txqlen(link, qlen);
+}
+
+void nl_cli_link_parse_weight(struct rtnl_link *link, char *arg)
+{
+	uint32_t weight = nl_cli_parse_u32(arg);
+	rtnl_link_set_weight(link, weight);
+}
+
+/** @} */
diff --git a/src/lib/neigh.c b/src/lib/neigh.c
new file mode 100644
index 0000000..a814bd8
--- /dev/null
+++ b/src/lib/neigh.c
@@ -0,0 +1,88 @@
+/*
+ * src/lib/neigh.c     CLI Neighbour Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_neigh Neighbour
+ *
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/neigh.h>
+
+struct rtnl_neigh *nl_cli_neigh_alloc(void)
+{
+	struct rtnl_neigh *neigh;
+
+	neigh = rtnl_neigh_alloc();
+	if (!neigh)
+		nl_cli_fatal(ENOMEM, "Unable to allocate neighbout object");
+
+	return neigh;
+}
+
+void nl_cli_neigh_parse_dst(struct rtnl_neigh *neigh, char *arg)
+{
+	struct nl_addr *a;
+	int err;
+
+	a = nl_cli_addr_parse(arg, rtnl_neigh_get_family(neigh));
+	if ((err = rtnl_neigh_set_dst(neigh, a)) < 0)
+		nl_cli_fatal(err, "Unable to set local address: %s",
+			nl_geterror(err));
+
+	nl_addr_put(a);
+}
+
+void nl_cli_neigh_parse_lladdr(struct rtnl_neigh *neigh, char *arg)
+{
+	struct nl_addr *a;
+
+	a = nl_cli_addr_parse(arg, AF_UNSPEC);
+	rtnl_neigh_set_lladdr(neigh, a);
+	nl_addr_put(a);
+}
+
+void nl_cli_neigh_parse_dev(struct rtnl_neigh *neigh,
+			    struct nl_cache *link_cache, char *arg)
+{
+	int ival;
+
+	if (!(ival = rtnl_link_name2i(link_cache, arg)))
+		nl_cli_fatal(ENOENT, "Link \"%s\" does not exist", arg);
+
+	rtnl_neigh_set_ifindex(neigh, ival);
+}
+
+void nl_cli_neigh_parse_family(struct rtnl_neigh *neigh, char *arg)
+{
+	int family;
+
+	if ((family = nl_str2af(arg)) == AF_UNSPEC)
+		nl_cli_fatal(EINVAL,
+			     "Unable to translate address family \"%s\"", arg);
+
+	rtnl_neigh_set_family(neigh, family);
+}
+
+void nl_cli_neigh_parse_state(struct rtnl_neigh *neigh, char *arg)
+{
+	int state;
+	
+	if ((state = rtnl_neigh_str2state(arg)) < 0)
+		nl_cli_fatal(state, "Unable to translate state \"%s\": %s",
+			arg, state);
+
+	rtnl_neigh_set_state(neigh, state);
+}
+
+/** @} */
diff --git a/src/lib/qdisc.c b/src/lib/qdisc.c
new file mode 100644
index 0000000..bc7ff92
--- /dev/null
+++ b/src/lib/qdisc.c
@@ -0,0 +1,72 @@
+/*
+ * src/lib/qdisc.c     CLI QDisc Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_qdisc Queueing Disciplines
+ *
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/qdisc.h>
+
+struct rtnl_qdisc *nl_cli_qdisc_alloc(void)
+{
+	struct rtnl_qdisc *qdisc;
+
+	qdisc = rtnl_qdisc_alloc();
+	if (!qdisc)
+		nl_cli_fatal(ENOMEM, "Unable to allocate qdisc object");
+
+	return qdisc;
+}
+
+void nl_cli_qdisc_parse_dev(struct rtnl_qdisc *qdisc, struct nl_cache *link_cache, char *arg)
+{
+	int ival;
+
+	if (!(ival = rtnl_link_name2i(link_cache, arg)))
+		nl_cli_fatal(ENOENT, "Link \"%s\" does not exist", arg);
+
+	rtnl_qdisc_set_ifindex(qdisc, ival);
+}
+
+void nl_cli_qdisc_parse_parent(struct rtnl_qdisc *qdisc, char *arg)
+{
+	uint32_t parent;
+	int err;
+
+	if ((err = rtnl_tc_str2handle(arg, &parent)) < 0)
+		nl_cli_fatal(err, "Unable to parse handle \"%s\": %s",
+		      arg, nl_geterror(err));
+
+	rtnl_qdisc_set_parent(qdisc, parent);
+}
+
+void nl_cli_qdisc_parse_handle(struct rtnl_qdisc *qdisc, char *arg)
+{
+	uint32_t handle;
+	int err;
+
+	if ((err = rtnl_tc_str2handle(arg, &handle)) < 0)
+		nl_cli_fatal(err, "Unable to parse handle \"%s\": %s",
+		      arg, nl_geterror(err));
+
+	rtnl_qdisc_set_handle(qdisc, handle);
+}
+
+void nl_cli_qdisc_parse_kind(struct rtnl_qdisc *qdisc, char *arg)
+{
+	rtnl_qdisc_set_kind(qdisc, arg);
+}
+
+/** @} */
diff --git a/src/lib/route.c b/src/lib/route.c
new file mode 100644
index 0000000..05cb2ad
--- /dev/null
+++ b/src/lib/route.c
@@ -0,0 +1,270 @@
+/*
+ * src/lib/route.c     CLI Route Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_route Routing
+ *
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/route.h>
+
+struct rtnl_route *nl_cli_route_alloc(void)
+{
+	struct rtnl_route *route;
+
+	route = rtnl_route_alloc();
+	if (!route)
+		nl_cli_fatal(ENOMEM, "Unable to allocate route object");
+
+	return route;
+}
+
+struct nl_cache *nl_cli_route_alloc_cache(struct nl_sock *sk, int flags)
+{
+	struct nl_cache *cache;
+	int err;
+
+	if ((err = rtnl_route_alloc_cache(sk, AF_UNSPEC, flags, &cache)) < 0)
+		nl_cli_fatal(err, "Unable to allocate route cache: %s\n",
+			     nl_geterror(err));
+
+	nl_cache_mngt_provide(cache);
+
+	return cache;
+}
+
+void nl_cli_route_parse_family(struct rtnl_route *route, char *arg)
+{
+	int family;
+
+	if ((family = nl_str2af(arg)) != AF_UNSPEC)
+		rtnl_route_set_family(route, family);
+}
+
+void nl_cli_route_parse_dst(struct rtnl_route *route, char *arg)
+{
+	struct nl_addr *addr;
+	int err;
+
+	addr = nl_cli_addr_parse(arg, rtnl_route_get_family(route));
+	if ((err = rtnl_route_set_dst(route, addr)) < 0)
+		nl_cli_fatal(err, "Unable to set destination address: %s",
+		      nl_geterror(err));
+
+	nl_addr_put(addr);
+}
+
+void nl_cli_route_parse_src(struct rtnl_route *route, char *arg)
+{
+	struct nl_addr *addr;
+	int err;
+
+	addr = nl_cli_addr_parse(arg, rtnl_route_get_family(route));
+	if ((err = rtnl_route_set_src(route, addr)) < 0)
+		nl_cli_fatal(err, "Unable to set source address: %s",
+		      nl_geterror(err));
+
+	nl_addr_put(addr);
+}
+
+void nl_cli_route_parse_pref_src(struct rtnl_route *route, char *arg)
+{
+	struct nl_addr *addr;
+	int err;
+
+	addr = nl_cli_addr_parse(arg, rtnl_route_get_family(route));
+	if ((err = rtnl_route_set_pref_src(route, addr)) < 0)
+		nl_cli_fatal(err, "Unable to set preferred source address: %s",
+		      nl_geterror(err));
+
+	nl_addr_put(addr);
+}
+
+void nl_cli_route_parse_metric(struct rtnl_route *route, char *subopts)
+{
+	/* strict equal order to RTAX_* */
+	static char *const tokens[] = {
+		"unspec",
+		"lock",
+		"mtu",
+		"window",
+		"rtt",
+		"rttvar",
+		"sstresh",
+		"cwnd",
+		"advmss",
+		"reordering",
+		"hoplimit",
+		"initcwnd",
+		"features",
+		NULL,
+	};
+	unsigned long lval;
+	char *arg, *endptr;
+
+	while (*subopts != '\0') {
+		int ret = getsubopt(&subopts, tokens, &arg);
+		if (ret == -1)
+			nl_cli_fatal(EINVAL, "Unknown metric token \"%s\"", arg);
+
+		if (ret == 0)
+			nl_cli_fatal(EINVAL, "Invalid metric \"%s\"", tokens[ret]);
+
+		if (arg == NULL)
+			nl_cli_fatal(EINVAL, "Metric \"%s\", no value given", tokens[ret]);
+
+		lval = strtoul(arg, &endptr, 0);
+		if (endptr == arg)
+			nl_cli_fatal(EINVAL, "Metric \"%s\", value not numeric", tokens[ret]);
+
+		if ((ret = rtnl_route_set_metric(route, ret, lval)) < 0)
+			nl_cli_fatal(ret, "Unable to set metric: %s",
+			      nl_geterror(ret));
+	}
+}
+
+void nl_cli_route_parse_nexthop(struct rtnl_route *route, char *subopts,
+		   struct nl_cache *link_cache)
+{
+	enum {
+		NH_DEV,
+		NH_VIA,
+		NH_WEIGHT,
+	};
+	static char *const tokens[] = {
+		"dev",
+		"via",
+		"weight",
+		NULL,
+	};
+	struct rtnl_nexthop *nh;
+	unsigned long lval;
+	struct nl_addr *addr;
+	int ival;
+	char *arg, *endptr;
+
+	if (!(nh = rtnl_route_nh_alloc()))
+		nl_cli_fatal(ENOMEM, "Out of memory");
+
+	while (*subopts != '\0') {
+		int ret = getsubopt(&subopts, tokens, &arg);
+		if (ret == -1)
+			nl_cli_fatal(EINVAL, "Unknown nexthop token \"%s\"", arg);
+
+		if (arg == NULL)
+			nl_cli_fatal(EINVAL, "Missing argument to option \"%s\"\n",
+				tokens[ret]);
+
+		switch (ret) {
+		case NH_DEV:
+			if (!(ival = rtnl_link_name2i(link_cache, arg)))
+				nl_cli_fatal(ENOENT,"Link \"%s\" does not exist", arg);
+
+			rtnl_route_nh_set_ifindex(nh, ival);
+			break;
+
+		case NH_VIA:
+			addr = nl_cli_addr_parse(arg,rtnl_route_get_family(route));
+			rtnl_route_nh_set_gateway(nh, addr);
+			nl_addr_put(addr);
+			break;
+
+		case NH_WEIGHT:
+			lval = strtoul(arg, &endptr, 0);
+			if (endptr == arg)
+				nl_cli_fatal(EINVAL,
+					     "Invalid weight \"%s\", not numeric",
+					     arg);
+			rtnl_route_nh_set_weight(nh, lval);
+			break;
+		}
+	}
+
+	rtnl_route_add_nexthop(route, nh);
+}
+
+void nl_cli_route_parse_table(struct rtnl_route *route, char *arg)
+{
+	unsigned long lval;
+	char *endptr;
+
+	lval = strtoul(arg, &endptr, 0);
+	if (endptr == arg) {
+		if ((lval = rtnl_route_str2table(arg)) < 0)
+			nl_cli_fatal(EINVAL, "Unknown table name \"%s\"", arg);
+	}
+
+	rtnl_route_set_table(route, lval);
+}
+
+void nl_cli_route_parse_prio(struct rtnl_route *route, char *arg)
+{
+	unsigned long lval;
+	char *endptr;
+
+	lval = strtoul(arg, &endptr, 0);
+	if (endptr == arg)
+		nl_cli_fatal(EINVAL, "Invalid priority value, not numeric");
+	rtnl_route_set_priority(route, lval);
+}
+
+void nl_cli_route_parse_scope(struct rtnl_route *route, char *arg)
+{
+	int ival;
+
+	if ((ival = rtnl_str2scope(arg)) < 0)
+		nl_cli_fatal(EINVAL, "Unknown routing scope \"%s\"", arg);
+
+	rtnl_route_set_scope(route, ival);
+}
+
+void nl_cli_route_parse_protocol(struct rtnl_route *route, char *arg)
+{
+	unsigned long lval;
+	char *endptr;
+
+	lval = strtoul(arg, &endptr, 0);
+	if (endptr == arg) {
+		if ((lval = rtnl_route_str2proto(arg)) < 0)
+			nl_cli_fatal(EINVAL,
+				     "Unknown routing protocol name \"%s\"",
+				     arg);
+	}
+
+	rtnl_route_set_protocol(route, lval);
+}
+
+void nl_cli_route_parse_type(struct rtnl_route *route, char *arg)
+{
+	int ival;
+
+	if ((ival = nl_str2rtntype(arg)) < 0)
+		nl_cli_fatal(EINVAL, "Unknown routing type \"%s\"", arg);
+
+	if ((ival = rtnl_route_set_type(route, ival)) < 0)
+		nl_cli_fatal(ival, "Unable to set routing type: %s",
+		      nl_geterror(ival));
+}
+
+void nl_cli_route_parse_iif(struct rtnl_route *route, char *arg, struct nl_cache *link_cache)
+{
+	int ival;
+
+	if (!(ival = rtnl_link_name2i(link_cache, arg)))
+		nl_cli_fatal(ENOENT, "Link \"%s\" does not exist", arg);
+
+	rtnl_route_set_iif(route, ival);
+}
+
+/** @} */
diff --git a/src/lib/rule.c b/src/lib/rule.c
new file mode 100644
index 0000000..96f1d4c
--- /dev/null
+++ b/src/lib/rule.c
@@ -0,0 +1,55 @@
+/*
+ * src/lib/rule.c     CLI Routing Rule Helpers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_rule Routing Rules
+ *
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/rule.h>
+
+struct rtnl_rule *nl_cli_rule_alloc(void)
+{
+	struct rtnl_rule *rule;
+
+	rule = rtnl_rule_alloc();
+	if (!rule)
+		nl_cli_fatal(ENOMEM, "Unable to allocate rule object");
+
+	return rule;
+}
+
+struct nl_cache *nl_cli_rule_alloc_cache(struct nl_sock *sk)
+{
+	struct nl_cache *cache;
+	int err;
+
+	if ((err = rtnl_rule_alloc_cache(sk, AF_UNSPEC, &cache)) < 0)
+		nl_cli_fatal(err, "Unable to allocate routing rule cache: %s\n",
+			     nl_geterror(err));
+
+	nl_cache_mngt_provide(cache);
+
+	return cache;
+}
+
+void nl_cli_rule_parse_family(struct rtnl_rule *rule, char *arg)
+{
+	int family;
+
+	if ((family = nl_str2af(arg)) != AF_UNSPEC)
+		rtnl_rule_set_family(rule, family);
+}
+
+/** @} */
diff --git a/src/lib/utils.c b/src/lib/utils.c
new file mode 100644
index 0000000..02a7be1
--- /dev/null
+++ b/src/lib/utils.c
@@ -0,0 +1,147 @@
+/*
+ * src/utils.c		Utilities
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @defgroup cli Command Line Interface API
+ *
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+
+uint32_t nl_cli_parse_u32(const char *arg)
+{
+	unsigned long lval;
+	char *endptr;
+
+	lval = strtoul(arg, &endptr, 0);
+	if (endptr == arg || lval == ULONG_MAX)
+		nl_cli_fatal(EINVAL, "Unable to parse \"%s\", not a number.",
+			     arg);
+
+	return (uint32_t) lval;
+}
+
+void nl_cli_print_version(void)
+{
+	printf("libnl tools version %s\n", LIBNL_VERSION);
+	printf(
+	"Copyright (C) 2003-2009 Thomas Graf <tgraf@redhat.com>\n"
+	"\n"
+	"This program comes with ABSOLUTELY NO WARRANTY. This is free \n"
+	"software, and you are welcome to redistribute it under certain\n"
+	"conditions. See the GNU General Public License for details.\n"
+	);
+
+	exit(0);
+}
+
+void nl_cli_fatal(int err, const char *fmt, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "Error: ");
+
+	if (fmt) {
+		va_start(ap, fmt);
+		vfprintf(stderr, fmt, ap);
+		va_end(ap);
+		fprintf(stderr, "\n");
+	} else
+		fprintf(stderr, "%s\n", strerror(err));
+
+	exit(abs(err));
+}
+
+int nl_cli_connect(struct nl_sock *sk, int protocol)
+{
+	int err;
+
+	if ((err = nl_connect(sk, protocol)) < 0)
+		nl_cli_fatal(err, "Unable to connect netlink socket: %s",
+			     nl_geterror(err));
+
+	return err;
+}
+
+struct nl_sock *nl_cli_alloc_socket(void)
+{
+	struct nl_sock *sock;
+
+	if (!(sock = nl_socket_alloc()))
+		nl_cli_fatal(ENOBUFS, "Unable to allocate netlink socket");
+
+	return sock;
+}
+
+struct nl_addr *nl_cli_addr_parse(const char *str, int family)
+{
+	struct nl_addr *addr;
+	int err;
+
+	if ((err = nl_addr_parse(str, family, &addr)) < 0)
+		nl_cli_fatal(err, "Unable to parse address \"%s\": %s",
+			     str, nl_geterror(err));
+
+	return addr;
+}
+
+int nl_cli_parse_dumptype(const char *str)
+{
+	if (!strcasecmp(str, "brief"))
+		return NL_DUMP_LINE;
+	else if (!strcasecmp(str, "details") || !strcasecmp(str, "detailed"))
+		return NL_DUMP_DETAILS;
+	else if (!strcasecmp(str, "stats"))
+		return NL_DUMP_STATS;
+	else if (!strcasecmp(str, "env"))
+		return NL_DUMP_ENV;
+	else
+		nl_cli_fatal(EINVAL, "Invalid dump type \"%s\".\n", str);
+
+	return 0;
+}
+
+int nl_cli_confirm(struct nl_object *obj, struct nl_dump_params *params,
+		   int default_yes)
+{
+	int answer;
+
+	nl_object_dump(obj, params);
+	printf("Delete? (%c/%c) ",
+		default_yes ? 'Y' : 'y',
+		default_yes ? 'n' : 'N');
+
+	do {
+		answer = tolower(getchar());
+		if (answer == '\n')
+			answer = default_yes ? 'y' : 'n';
+	} while (answer != 'y' && answer != 'n');
+
+	return answer == 'y';
+}
+
+struct nl_cache *nl_cli_alloc_cache(struct nl_sock *sock, const char *name,
+			    int (*ac)(struct nl_sock *, struct nl_cache **))
+{
+	struct nl_cache *cache;
+	int err;
+
+	if ((err = ac(sock, &cache)) < 0)
+		nl_cli_fatal(err, "Unable to allocate %s cache: %s",
+			     name, nl_geterror(err));
+
+	nl_cache_mngt_provide(cache);
+
+	return cache;
+}
+
+/** @} */
diff --git a/src/nf-ct-dump.c b/src/nf-ct-dump.c
deleted file mode 100644
index 54ee4c7..0000000
--- a/src/nf-ct-dump.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * src/nf-ct-dump.c     Dump conntrack attributes
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
- * Copyright (c) 2007 Secure Computing Corporation
- */
-
-#include "utils.h"
-#include <netlink/netfilter/ct.h>
-#include <linux/netfilter/nf_conntrack_common.h>
-
-#include "f_ct.c"
-
-static void print_usage(void)
-{
-	printf(
-	"Usage: nf-ct-dump <mode> [<filter>]\n"
-	"  mode := { brief | detailed | stats | xml }\n"
-	"  filter := [family FAMILY] [proto PROTO] [tcpstate TCPSTATE]\n"
-	"            [status STATUS] [timeout TIMEOUT] [mark MARK] [use USE] [id ID]\n"
-	"            [origsrc ADDR] [origdst ADDR] [origsrcport PORT] [origdstport PORT]\n"
-	"            [origicmpid ID] [origicmptype TYPE] [origicmpcode CODE]\n"
-	"            [origpackets PACKETS] [origbytes BYTES]\n"
-	"            [replysrc ADDR] [replydst ADDR] [replysrcport PORT] [replydstport PORT]\n"
-	"            [replyicmpid ID] [replyicmptype TYPE] [replyicmpcode CODE]\n"
-	"            [replypackets PACKETS] [replybytes BYTES]\n"
-	"            [{ replied | unreplied }] [{ assured | unassured }]\n"
-	);
-	exit(1);
-}
-
-int main(int argc, char *argv[])
-{
-	struct nl_handle *nlh;
-	struct nl_cache *ct_cache;
-	struct nfnl_ct *ct;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF
-	};
-	int err = 1;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
-
-	ct = nfnl_ct_alloc();
-	if (!ct)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_NETFILTER) < 0)
-		goto errout_free;
-
-	ct_cache = nfnl_ct_alloc_cache(nlh);
-        if (!ct_cache) {
-		fprintf(stderr, "Unable to retrieve ct cache: %s\n",
-			nl_geterror());
-		goto errout_close;
-	}
-	nl_cache_mngt_provide(ct_cache);
-
-	params.dp_type = nltool_parse_dumptype(argv[1]);
-	if (params.dp_type < 0)
-		goto errout_ct_cache;
-
-	get_filter(ct, argc, argv, 2);
-	nl_cache_dump_filter(ct_cache, &params, (struct nl_object *) ct);
-
-	err = 0;
-
-errout_ct_cache:
-	nl_cache_free(ct_cache);
-errout_close:
-	nl_close(nlh);
-errout_free:
-	nfnl_ct_put(ct);
-errout:
-	return err;
-}
diff --git a/src/nf-ct-list.c b/src/nf-ct-list.c
new file mode 100644
index 0000000..5f72998
--- /dev/null
+++ b/src/nf-ct-list.c
@@ -0,0 +1,136 @@
+/*
+ * src/nf-ct-list.c     List Conntrack Entries
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/ct.h>
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nf-ct-list [OPTION]... [CONNTRACK ENTRY]\n"
+	"\n"
+	"Options\n"
+	" -f, --format=TYPE     Output format { brief | details | stats }\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	"\n"
+	"Conntrack Selection\n"
+	" -i, --id=NUM            Identifier\n"
+	" -p, --proto=PROTOCOL    Protocol\n"
+	"     --tcp-state=STATE   TCP connection state\n"
+	"     --orig-src=ADDR     Original source address\n"
+	"     --orig-sport=PORT   Original source port\n"
+	"     --orig-dst=ADDR     Original destination address\n"
+	"     --orig-dport=PORT   Original destination port\n"
+	"     --reply-src=ADDR    Reply source address\n"
+	"     --reply-sport=PORT  Reply source port\n"
+	"     --reply-dst=ADDR    Reply destination address\n"
+	"     --reply-dport=PORT  Reply destination port\n"
+	" -F, --family=FAMILY     Address family\n"
+	"     --mark=NUM          Mark value\n"
+	"     --timeout=NUM       Timeout value\n"
+	"     --refcnt=NUM        Use counter value\n"
+	"     --flags             Flags\n"
+	);
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct nl_cache *ct_cache;
+	struct nfnl_ct *ct;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+ 
+ 	ct = nl_cli_ct_alloc();
+ 
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_MARK = 257,
+			ARG_TCP_STATE = 258,
+			ARG_ORIG_SRC,
+			ARG_ORIG_SPORT,
+			ARG_ORIG_DST,
+			ARG_ORIG_DPORT,
+			ARG_REPLY_SRC,
+			ARG_REPLY_SPORT,
+			ARG_REPLY_DST,
+			ARG_REPLY_DPORT,
+			ARG_TIMEOUT,
+			ARG_REFCNT,
+			ARG_FLAGS,
+		};
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "id", 1, 0, 'i' },
+			{ "proto", 1, 0, 'p' },
+			{ "tcp-state", 1, 0, ARG_TCP_STATE },
+			{ "orig-src", 1, 0, ARG_ORIG_SRC },
+			{ "orig-sport", 1, 0, ARG_ORIG_SPORT },
+			{ "orig-dst", 1, 0, ARG_ORIG_DST },
+			{ "orig-dport", 1, 0, ARG_ORIG_DPORT },
+			{ "reply-src", 1, 0, ARG_REPLY_SRC },
+			{ "reply-sport", 1, 0, ARG_REPLY_SPORT },
+			{ "reply-dst", 1, 0, ARG_REPLY_DST },
+			{ "reply-dport", 1, 0, ARG_REPLY_DPORT },
+			{ "family", 1, 0, 'F' },
+			{ "mark", 1, 0, ARG_MARK },
+			{ "timeout", 1, 0, ARG_TIMEOUT },
+			{ "refcnt", 1, 0, ARG_REFCNT },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case '?': exit(NLE_INVAL);
+		case '4': nfnl_ct_set_family(ct, AF_INET); break;
+		case '6': nfnl_ct_set_family(ct, AF_INET6); break;
+		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'i': nl_cli_ct_parse_id(ct, optarg); break;
+		case 'p': nl_cli_ct_parse_protocol(ct, optarg); break;
+		case ARG_TCP_STATE: nl_cli_ct_parse_tcp_state(ct, optarg); break;
+		case ARG_ORIG_SRC: nl_cli_ct_parse_src(ct, 0, optarg); break;
+		case ARG_ORIG_SPORT: nl_cli_ct_parse_src_port(ct, 0, optarg); break;
+		case ARG_ORIG_DST: nl_cli_ct_parse_dst(ct, 0, optarg); break;
+		case ARG_ORIG_DPORT: nl_cli_ct_parse_dst_port(ct, 0, optarg); break;
+		case ARG_REPLY_SRC: nl_cli_ct_parse_src(ct, 1, optarg); break;
+		case ARG_REPLY_SPORT: nl_cli_ct_parse_src_port(ct, 1, optarg); break;
+		case ARG_REPLY_DST: nl_cli_ct_parse_dst(ct, 1, optarg); break;
+		case ARG_REPLY_DPORT: nl_cli_ct_parse_dst_port(ct, 1, optarg); break;
+		case 'F': nl_cli_ct_parse_family(ct, optarg); break;
+		case ARG_MARK: nl_cli_ct_parse_mark(ct, optarg); break;
+		case ARG_TIMEOUT: nl_cli_ct_parse_timeout(ct, optarg); break;
+		case ARG_REFCNT: nl_cli_ct_parse_use(ct, optarg); break;
+		case ARG_FLAGS: nl_cli_ct_parse_status(ct, optarg); break;
+		}
+ 	}
+
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_NETFILTER);
+	ct_cache = nl_cli_ct_alloc_cache(sock);
+
+	nl_cache_dump_filter(ct_cache, &params, OBJ_CAST(ct));
+
+	return 0;
+}
diff --git a/src/nf-log.c b/src/nf-log.c
index 2c100f8..26bae6d 100644
--- a/src/nf-log.c
+++ b/src/nf-log.c
@@ -6,18 +6,28 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
  */
 
-#include <sys/types.h>
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
 #include <linux/netfilter/nfnetlink_log.h>
-
-#include "utils.h"
 #include <netlink/netfilter/nfnl.h>
 #include <netlink/netfilter/log.h>
 
+static struct nfnl_log *alloc_log(void)
+{
+	struct nfnl_log *log;
+
+	log = nfnl_log_alloc();
+	if (!log)
+		nl_cli_fatal(ENOMEM, "Unable to allocate log object");
+
+	return log;
+}
+
 static void obj_input(struct nl_object *obj, void *arg)
 {
 	struct nl_dump_params dp = {
@@ -40,74 +50,73 @@
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nfnlh;
-	struct nl_handle *rtnlh;
+	struct nl_sock *nf_sock;
+	struct nl_sock *rt_sock;
         struct nl_cache *link_cache;
-	int err = 1;
-	int family, group;
+	struct nfnl_log *log;
+	enum nfnl_log_copy_mode copy_mode;
+	uint32_t copy_range;
+	int err;
+	int family;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	nfnlh = nltool_alloc_handle();
-	if (nfnlh == NULL)
-		return -1;
-
-	nl_disable_sequence_check(nfnlh);
-
-	nl_socket_modify_cb(nfnlh, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+	nf_sock = nl_cli_alloc_socket();
+	nl_socket_disable_seq_check(nf_sock);
+	nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
 
 	if ((argc > 1 && !strcasecmp(argv[1], "-h")) || argc < 3) {
-		printf("Usage: nf-log family group\n");
+		printf("Usage: nf-log family group [ copy_mode ] "
+		       "[copy_range] \n");
 		return 2;
 	}
 
-	if (nfnl_connect(nfnlh) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout;
-	}
+	nl_cli_connect(nf_sock, NETLINK_NETFILTER);
 
 	family = nl_str2af(argv[1]);
-	if (family == AF_UNSPEC) {
-		fprintf(stderr, "Unknown family: %s\n", argv[1]);
-		goto errout;
+	if (family == AF_UNSPEC)
+		nl_cli_fatal(NLE_INVAL, "Unknown family \"%s\": %s",
+			     argv[1], nl_geterror(family));
+
+	nfnl_log_pf_unbind(nf_sock, family);
+	if ((err = nfnl_log_pf_bind(nf_sock, family)) < 0)
+		nl_cli_fatal(err, "Unable to bind logger: %s",
+			     nl_geterror(err));
+
+	log = alloc_log();
+	nfnl_log_set_group(log, atoi(argv[2]));
+
+	copy_mode = NFNL_LOG_COPY_META;
+	if (argc > 3) {
+		copy_mode = nfnl_log_str2copy_mode(argv[3]);
+		if (copy_mode < 0)
+			nl_cli_fatal(copy_mode,
+				     "Unable to parse copy mode \"%s\": %s",
+				     argv[3], nl_geterror(copy_mode));
 	}
-	if (nfnl_log_pf_unbind(nfnlh, family) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout;
-	}
-	if (nfnl_log_pf_bind(nfnlh, family) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout;
+	nfnl_log_set_copy_mode(log, copy_mode);
+
+	copy_range = 0xFFFF;
+	if (argc > 4)
+		copy_mode = atoi(argv[4]);
+	nfnl_log_set_copy_range(log, copy_range);
+
+	if ((err = nfnl_log_create(nf_sock, log)) < 0)
+		nl_cli_fatal(err, "Unable to bind instance: %s",
+			     nl_geterror(err));
+
+	{
+		struct nl_dump_params dp = {
+			.dp_type = NL_DUMP_STATS,
+			.dp_fd = stdout,
+			.dp_dump_msgtype = 1,
+		};
+
+		printf("log params: ");
+		nl_object_dump((struct nl_object *) log, &dp);
 	}
 
-	group = nl_str2af(argv[2]);
-	if (nfnl_log_bind(nfnlh, group) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout;
-	}
-
-	if (nfnl_log_set_mode(nfnlh, 0, NFULNL_COPY_PACKET, 0xffff) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout;
-	}
-
-	rtnlh = nltool_alloc_handle();
-	if (rtnlh == NULL) {
-		goto errout_close;
-	}
-
-	if (nl_connect(rtnlh, NETLINK_ROUTE) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout;
-	}
-
-	if ((link_cache = rtnl_link_alloc_cache(rtnlh)) == NULL) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout_close;
-	}
-
-	nl_cache_mngt_provide(link_cache);
+	rt_sock = nl_cli_alloc_socket();
+	nl_cli_connect(rt_sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(rt_sock);
 
 	while (1) {
 		fd_set rfds;
@@ -115,28 +124,24 @@
 
 		FD_ZERO(&rfds);
 
-		maxfd = nffd = nl_socket_get_fd(nfnlh);
+		maxfd = nffd = nl_socket_get_fd(nf_sock);
 		FD_SET(nffd, &rfds);
 
-		rtfd = nl_socket_get_fd(rtnlh);
+		rtfd = nl_socket_get_fd(rt_sock);
 		FD_SET(rtfd, &rfds);
 		if (maxfd < rtfd)
 			maxfd = rtfd;
 
-		/* wait for an incoming message on the netlink socket */
+		/* wait for an incoming message on the netlink nf_socket */
 		retval = select(maxfd+1, &rfds, NULL, NULL, NULL);
 
 		if (retval) {
 			if (FD_ISSET(nffd, &rfds))
-				nl_recvmsgs_default(nfnlh);
+				nl_recvmsgs_default(nf_sock);
 			if (FD_ISSET(rtfd, &rfds))
-				nl_recvmsgs_default(rtnlh);
+				nl_recvmsgs_default(rt_sock);
 		}
 	}
 
-	nl_close(rtnlh);
-errout_close:
-	nl_close(nfnlh);
-errout:
-	return err;
+	return 0;
 }
diff --git a/src/nf-monitor.c b/src/nf-monitor.c
index 2bc58c9..fe99af4 100644
--- a/src/nf-monitor.c
+++ b/src/nf-monitor.c
@@ -6,12 +6,12 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
  * Copyright (c) 2007 Secure Computing Corporation
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
 #include <netlink/netfilter/nfnl.h>
 
 static void obj_input(struct nl_object *obj, void *arg)
@@ -36,67 +36,59 @@
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
-	int err = 1;
+	struct nl_sock *sock;
+	int err;
 	int i, idx;
 
 	static const struct {
 		enum nfnetlink_groups gr_id;
 		const char* gr_name;
-	} known_groups[] = {
+	} groups[] = {
 		{ NFNLGRP_CONNTRACK_NEW, "ct-new" },
 		{ NFNLGRP_CONNTRACK_UPDATE, "ct-update" },
 		{ NFNLGRP_CONNTRACK_DESTROY, "ct-destroy" },
 		{ NFNLGRP_NONE, NULL }
 	};
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	nlh = nltool_alloc_handle();
-	if (nlh == NULL)
-		return -1;
-
-	nl_disable_sequence_check(nlh);
-
-	nl_socket_modify_cb(nlh, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+	sock = nl_cli_alloc_socket();
+	nl_socket_disable_seq_check(sock);
+	nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
 
 	if (argc > 1 && !strcasecmp(argv[1], "-h")) {
 		printf("Usage: nf-monitor [<groups>]\n");
 
 		printf("Known groups:");
-		for (i = 0; known_groups[i].gr_id != NFNLGRP_NONE; i++)
-			printf(" %s", known_groups[i].gr_name);
+		for (i = 0; groups[i].gr_id != NFNLGRP_NONE; i++)
+			printf(" %s", groups[i].gr_name);
 		printf("\n");
 		return 2;
 	}
 
-	if (nfnl_connect(nlh) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout;
-	}
+	nl_cli_connect(sock, NETLINK_NETFILTER);
 
 	for (idx = 1; argc > idx; idx++) {
-		for (i = 0; known_groups[i].gr_id != NFNLGRP_NONE; i++) {
-			if (!strcmp(argv[idx], known_groups[i].gr_name)) {
+		for (i = 0; groups[i].gr_id != NFNLGRP_NONE; i++) {
+			if (strcmp(argv[idx], groups[i].gr_name))
+				continue;
 
-				if (nl_socket_add_membership(nlh, known_groups[i].gr_id) < 0) {
-					fprintf(stderr, "%s: %s\n", argv[idx], nl_geterror());
-					goto errout;
-				}
-
-				break;
-			}
+			err = nl_socket_add_membership(sock, groups[i].gr_id);
+			if (err < 0)
+				nl_cli_fatal(err,
+					     "Unable to add membership: %s",
+					     nl_geterror(err));
+			break;
 		}
-		if (known_groups[i].gr_id == NFNLGRP_NONE)
-			fprintf(stderr, "Warning: Unknown group: %s\n", argv[idx]);
+
+		if (groups[i].gr_id == NFNLGRP_NONE)
+			nl_cli_fatal(NLE_OBJ_NOTFOUND, "Unknown group: \"%s\"",
+				     argv[idx]);
 	}
 
 	while (1) {
 		fd_set rfds;
 		int fd, retval;
 
-		fd = nl_socket_get_fd(nlh);
+		fd = nl_socket_get_fd(sock);
 
 		FD_ZERO(&rfds);
 		FD_SET(fd, &rfds);
@@ -105,11 +97,9 @@
 
 		if (retval) {
 			/* FD_ISSET(fd, &rfds) will be true */
-			nl_recvmsgs_default(nlh);
+			nl_recvmsgs_default(sock);
 		}
 	}
 
-	nl_close(nlh);
-errout:
-	return err;
+	return 0;
 }
diff --git a/src/nf-queue.c b/src/nf-queue.c
new file mode 100644
index 0000000..3fb3c11
--- /dev/null
+++ b/src/nf-queue.c
@@ -0,0 +1,172 @@
+/*
+ * src/nf-queue.c     Monitor netfilter queue events
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2010  Karl Hiramoto <karl@hiramoto.org>
+ */
+
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/queue.h>
+#include <netlink/netfilter/queue_msg.h>
+
+static struct nl_sock *nf_sock;
+
+static struct nfnl_queue *alloc_queue(void)
+{
+	struct nfnl_queue *queue;
+
+	queue = nfnl_queue_alloc();
+	if (!queue)
+		nl_cli_fatal(ENOMEM, "Unable to allocate queue object");
+
+	return queue;
+}
+
+
+static void obj_input(struct nl_object *obj, void *arg)
+{
+	struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) obj;
+	struct nl_dump_params dp = {
+		.dp_type = NL_DUMP_STATS,
+		.dp_fd = stdout,
+		.dp_dump_msgtype = 1,
+	};
+	uint32_t packet_id = nfnl_queue_msg_get_packetid(msg);
+	static uint32_t next_packet_id = 0;
+	struct nfnl_queue_msg *lost_msg = NULL;
+	uint8_t family;
+	uint16_t group;
+
+	if (packet_id > next_packet_id) {
+		printf("Warning: %d Out of order packets.  Queue or socket overload \n", packet_id - next_packet_id);
+		group = nfnl_queue_msg_get_group(msg);
+		family = nfnl_queue_msg_get_family(msg);
+		lost_msg = nfnl_queue_msg_alloc();
+
+		do {
+			nfnl_queue_msg_set_group(lost_msg, group);
+			nfnl_queue_msg_set_family(lost_msg, family);
+			nfnl_queue_msg_set_packetid(lost_msg, next_packet_id);
+			nfnl_queue_msg_set_verdict(lost_msg, NF_ACCEPT);
+			nfnl_queue_msg_send_verdict(nf_sock, lost_msg);
+			next_packet_id++;
+		} while (packet_id > next_packet_id);
+
+		nfnl_queue_msg_put(lost_msg);
+	}
+
+	next_packet_id = packet_id + 1;
+	nfnl_queue_msg_set_verdict(msg, NF_ACCEPT);
+	nl_object_dump(obj, &dp);
+	nfnl_queue_msg_send_verdict(nf_sock, msg);
+}
+
+static int event_input(struct nl_msg *msg, void *arg)
+{
+	if (nl_msg_parse(msg, &obj_input, NULL) < 0)
+		fprintf(stderr, "<<EVENT>> Unknown message type\n");
+
+	/* Exit nl_recvmsgs_def() and return to the main select() */
+	return NL_STOP;
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *rt_sock;
+	struct nl_cache *link_cache;
+	struct nfnl_queue *queue;
+	enum nfnl_queue_copy_mode copy_mode;
+	uint32_t copy_range;
+	int err = 1;
+	int family;
+
+	nf_sock = nfnl_queue_socket_alloc();
+	if (nf_sock == NULL)
+		nl_cli_fatal(ENOBUFS, "Unable to allocate netlink socket");
+
+	nl_socket_disable_seq_check(nf_sock);
+	nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+
+	if ((argc > 1 && !strcasecmp(argv[1], "-h")) || argc < 3) {
+		printf("Usage: nf-queue family group [ copy_mode ] "
+		       "[ copy_range ]\n");
+		printf("family: [ inet | inet6 | ... ] \n");
+		printf("group: the --queue-num arg that you gave to iptables\n");
+		printf("copy_mode: [ none | meta | packet ] \n");
+		return 2;
+	}
+
+	nl_cli_connect(nf_sock, NETLINK_NETFILTER);
+
+	if ((family = nl_str2af(argv[1])) == AF_UNSPEC)
+		nl_cli_fatal(NLE_INVAL, "Unknown family \"%s\"", argv[1]);
+
+	nfnl_queue_pf_unbind(nf_sock, family);
+	if ((err = nfnl_queue_pf_bind(nf_sock, family)) < 0)
+		nl_cli_fatal(err, "Unable to bind logger: %s",
+			     nl_geterror(err));
+
+	queue = alloc_queue();
+	nfnl_queue_set_group(queue, atoi(argv[2]));
+
+	copy_mode = NFNL_QUEUE_COPY_PACKET;
+	if (argc > 3) {
+		copy_mode = nfnl_queue_str2copy_mode(argv[3]);
+		if (copy_mode < 0)
+			nl_cli_fatal(copy_mode,
+				     "Unable to parse copy mode \"%s\": %s",
+				     argv[3], nl_geterror(copy_mode));
+	}
+	nfnl_queue_set_copy_mode(queue, copy_mode);
+
+	copy_range = 0xFFFF;
+	if (argc > 4)
+		copy_range = atoi(argv[4]);
+	nfnl_queue_set_copy_range(queue, copy_range);
+
+	if ((err = nfnl_queue_create(nf_sock, queue)) < 0)
+		nl_cli_fatal(err, "Unable to bind queue: %s", nl_geterror(err));
+
+	rt_sock = nl_cli_alloc_socket();
+	nl_cli_connect(rt_sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(rt_sock);
+
+	nl_socket_set_buffer_size(nf_sock, 1024*127, 1024*127);
+
+	while (1) {
+		fd_set rfds;
+		int nffd, rtfd, maxfd, retval;
+
+		FD_ZERO(&rfds);
+
+		maxfd = nffd = nl_socket_get_fd(nf_sock);
+		FD_SET(nffd, &rfds);
+
+		rtfd = nl_socket_get_fd(rt_sock);
+		FD_SET(rtfd, &rfds);
+		if (maxfd < rtfd)
+			maxfd = rtfd;
+
+		/* wait for an incoming message on the netlink socket */
+		retval = select(maxfd+1, &rfds, NULL, NULL, NULL);
+
+		if (retval) {
+			if (FD_ISSET(nffd, &rfds))
+				nl_recvmsgs_default(nf_sock);
+			if (FD_ISSET(rtfd, &rfds))
+				nl_recvmsgs_default(rt_sock);
+		}
+	}
+
+	return 0;
+}
diff --git a/src/nl-addr-add.c b/src/nl-addr-add.c
index de5ddcd..52995ec 100644
--- a/src/nl-addr-add.c
+++ b/src/nl-addr-add.c
@@ -2,67 +2,119 @@
  * src/nl-addr-add.c     Add addresses
  *
  *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation version 2 of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/addr.h>
+#include <netlink/cli/link.h>
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+	printf(
+"Usage: nl-addr-add [OPTION]... [ADDRESS]\n"
+"\n"
+"Options\n"
+"     --replace             Replace the address if it exists.\n"
+" -q, --quiet               Do not print informal notifications.\n"
+" -h, --help                Show this help.\n"
+" -v, --version             Show versioning information.\n"
+"\n"
+"Address Options\n"
+" -a, --local=ADDR          Address to be considered local.\n"
+" -d, --dev=DEV             Device the address should be assigned to.\n"
+"     --family=FAMILY       Address family (normally autodetected).\n"
+"     --broadcast=ADDR      Broadcast address of network (IPv4).\n"
+"     --peer=ADDR           Peer address (IPv4).\n"
+"     --label=STRING        Additional address label (IPv4).\n"
+"     --scope=SCOPE         Scope of local address (IPv4).\n"
+"     --preferred=TIME      Preferred lifetime (IPv6).\n"
+"     --valid=TIME          Valid lifetime (IPv6).\n"
+	);
+
+	exit(0);
+}
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
+	struct nl_sock *sock;
 	struct rtnl_addr *addr;
-	struct nl_addr *local;
-	int err = 1;
+	struct nl_cache *link_cache;
+	struct nl_dump_params dp = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+	int err, nlflags = NLM_F_CREATE;
+ 
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+ 	addr = nl_cli_addr_alloc();
+ 
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_LABEL = 258,
+			ARG_PEER,
+			ARG_SCOPE,
+			ARG_BROADCAST,
+			ARG_REPLACE,
+			ARG_PREFERRED,
+			ARG_VALID,
+		};
+		static struct option long_opts[] = {
+			{ "replace", 0, 0, ARG_REPLACE },
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "local", 1, 0, 'a' },
+			{ "dev", 1, 0, 'd' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ "label", 1, 0, ARG_LABEL },
+			{ "peer", 1, 0, ARG_PEER },
+			{ "scope", 1, 0, ARG_SCOPE },
+			{ "broadcast", 1, 0, ARG_BROADCAST },
+			{ "preferred", 1, 0, ARG_PREFERRED },
+			{ "valid", 1, 0, ARG_VALID },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "qhva:d:", long_opts, &optidx);
+		if (c == -1)
+			break;
 
-	if (argc < 3 || !strcmp(argv[1], "-h")) {
-		printf("Usage: nl-addr-add <addr> <ifindex>\n");
-		goto errout;
-	}
+		switch (c) {
+		case '?': exit(NLE_INVAL);
+		case ARG_REPLACE: nlflags |= NLM_F_REPLACE; break;
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'a': nl_cli_addr_parse_local(addr, optarg); break;
+		case 'd': nl_cli_addr_parse_dev(addr, link_cache, optarg); break;
+		case ARG_FAMILY: nl_cli_addr_parse_family(addr, optarg); break;
+		case ARG_LABEL: nl_cli_addr_parse_label(addr, optarg); break;
+		case ARG_PEER: nl_cli_addr_parse_peer(addr, optarg); break;
+		case ARG_SCOPE: nl_cli_addr_parse_scope(addr, optarg); break;
+		case ARG_BROADCAST: nl_cli_addr_parse_broadcast(addr, optarg); break;
+		case ARG_PREFERRED: nl_cli_addr_parse_preferred(addr, optarg); break;
+		case ARG_VALID: nl_cli_addr_parse_valid(addr, optarg); break;
+		}
+ 	}
 
-	if (nltool_init(argc, argv) < 0)
-		goto errout;
+	if ((err = rtnl_addr_add(sock, addr, nlflags)) < 0)
+		nl_cli_fatal(err, "Unable to add address: %s",
+			     nl_geterror(err));
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		goto errout;
+	if (!quiet) {
+		printf("Added ");
+		nl_object_dump(OBJ_CAST(addr), &dp);
+ 	}
 
-	addr = rtnl_addr_alloc();
-	if (!addr)
-		goto errout_free_handle;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free_addr;
-
-	local = nltool_addr_parse(argv[1]);
-	if (!local)
-		goto errout_close;
-
-	if (rtnl_addr_set_local(addr, local) < 0) {
-		fprintf(stderr, "Unable to set local address: %s\n",
-			nl_geterror());
-		goto errout_put_addr;
-	}
-
-	rtnl_addr_set_ifindex(addr, strtoul(argv[2], NULL, 0));
-
-	if (rtnl_addr_add(nlh, addr, 0) < 0) {
-		fprintf(stderr, "Unable to add address: %s\n", nl_geterror());
-		goto errout_close;
-	}
-
-	err = 0;
-errout_put_addr:
-	nl_addr_put(local);
-errout_close:
-	nl_close(nlh);
-errout_free_addr:
-	rtnl_addr_put(addr);
-errout_free_handle:
-	nl_handle_destroy(nlh);
-errout:
-	return err;
+	return 0;
 }
diff --git a/src/nl-addr-delete.c b/src/nl-addr-delete.c
index 19ee6c5..2849c01 100644
--- a/src/nl-addr-delete.c
+++ b/src/nl-addr-delete.c
@@ -2,69 +2,139 @@
  * src/nl-addr-delete.c     Delete addresses
  *
  *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation version 2 of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/addr.h>
+#include <netlink/cli/link.h>
+
+static struct nl_sock *sock;
+static int interactive = 0, default_yes = 0, quiet = 0;
+static int deleted = 0;
+
+static void print_usage(void)
+{
+	printf(
+"Usage: nl-addr-delete [OPTION]... [ADDRESS]\n"
+"\n"
+"Options\n"
+" -i, --interactive         Run interactively.\n"
+"     --yes                 Set default answer to yes.\n"
+" -q, --quiet               Do not print informal notifications.\n"
+" -h, --help                Show this help.\n"
+" -v, --version             Show versioning information.\n"
+"\n"
+"Address Options\n"
+" -a, --local=ADDR          Local address.\n"
+" -d, --dev=DEV             Associated network device.\n"
+"     --family=FAMILY       Family of local address.\n"
+"     --label=STRING        Address label (IPv4).\n"
+"     --peer=ADDR           Peer address (IPv4).\n"
+"     --scope=SCOPE         Address scope (IPv4).\n"
+"     --broadcast=ADDR      Broadcast address of network (IPv4).\n"
+"     --valid-lifetime=TS   Valid lifetime before route expires (IPv6).\n"
+"     --preferred=TIME      Preferred lifetime (IPv6).\n"
+"     --valid=TIME          Valid lifetime (IPv6).\n"
+	);
+
+	exit(0);
+}
+
+static void delete_cb(struct nl_object *obj, void *arg)
+{
+	struct rtnl_addr *addr = nl_object_priv(obj);
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+	int err;
+
+	if (interactive && !nl_cli_confirm(obj, &params, default_yes))
+		return;
+
+	if ((err = rtnl_addr_delete(sock, addr, 0)) < 0)
+		nl_cli_fatal(err, "Unable to delete address: %s\n",
+			     nl_geterror(err));
+
+	if (!quiet) {
+		printf("Deleted ");
+		nl_object_dump(obj, &params);
+	}
+
+	deleted++;
+}
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
 	struct rtnl_addr *addr;
-	struct nl_addr *local;
-	int err = 1;
+	struct nl_cache *link_cache, *addr_cache;
 
-	if (argc < 3 || !strcmp(argv[1], "-h")) {
-		printf("Usage: nl-addr-delete <addr> <ifindex>\n");
-		goto errout;
-	}
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	addr_cache = nl_cli_addr_alloc_cache(sock);
+	addr = nl_cli_addr_alloc();
 
-	if (nltool_init(argc, argv) < 0)
-		goto errout;
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_LABEL = 258,
+			ARG_YES,
+			ARG_PEER,
+			ARG_SCOPE,
+			ARG_BROADCAST,
+			ARG_PREFERRED,
+			ARG_VALID,
+		};
+		static struct option long_opts[] = {
+			{ "interactive", 0, 0, 'i' },
+			{ "yes", 0, 0, ARG_YES },
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "local", 1, 0, 'a' },
+			{ "dev", 1, 0, 'd' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ "label", 1, 0, ARG_LABEL },
+			{ "peer", 1, 0, ARG_PEER },
+			{ "scope", 1, 0, ARG_SCOPE },
+			{ "broadcast", 1, 0, ARG_BROADCAST },
+			{ "preferred", 1, 0, ARG_PREFERRED },
+			{ "valid", 1, 0, ARG_VALID },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "iqhva:d:", long_opts, &optidx);
+		if (c == -1)
+			break;
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		goto errout;
+		switch (c) {
+		case 'i': interactive = 1; break;
+		case ARG_YES: default_yes = 1; break;
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'a': nl_cli_addr_parse_local(addr, optarg); break;
+		case 'd': nl_cli_addr_parse_dev(addr, link_cache, optarg); break;
+		case ARG_FAMILY: nl_cli_addr_parse_family(addr, optarg); break;
+		case ARG_LABEL: nl_cli_addr_parse_label(addr, optarg); break;
+		case ARG_PEER: nl_cli_addr_parse_peer(addr, optarg); break;
+		case ARG_SCOPE: nl_cli_addr_parse_scope(addr, optarg); break;
+		case ARG_BROADCAST: nl_cli_addr_parse_broadcast(addr, optarg); break;
+		case ARG_PREFERRED: nl_cli_addr_parse_preferred(addr, optarg); break;
+		case ARG_VALID: nl_cli_addr_parse_valid(addr, optarg); break;
+		}
+ 	}
 
-	addr = rtnl_addr_alloc();
-	if (!addr)
-		goto errout_free_handle;
+	nl_cache_foreach_filter(addr_cache, OBJ_CAST(addr), delete_cb, NULL);
 
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free_addr;
+	if (!quiet)
+		printf("Deleted %d addresses\n", deleted);
 
-	local = nltool_addr_parse(argv[1]);
-	if (!local)
-		goto errout_close;
-
-	if (rtnl_addr_set_local(addr, local) < 0) {
-		fprintf(stderr, "Unable to set local address: %s\n",
-			nl_geterror());
-		goto errout_addr_put;
-	}
-
-	rtnl_addr_set_ifindex(addr, strtoul(argv[2], NULL, 0));
-
-	if (rtnl_addr_delete(nlh, addr, 0) < 0) {
-		fprintf(stderr, "Unable to delete address: %s\n",
-			nl_geterror());
-		goto errout_addr_put;
-	}
-
-	err = 0;
-
-errout_addr_put:
-	nl_addr_put(local);
-errout_close:
-	nl_close(nlh);
-errout_free_addr:
-	rtnl_addr_put(addr);
-errout_free_handle:
-	nl_handle_destroy(nlh);
-errout:
-	return err;
+	return 0;
 }
diff --git a/src/nl-addr-dump.c b/src/nl-addr-dump.c
deleted file mode 100644
index 5dcd53b..0000000
--- a/src/nl-addr-dump.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * src/nl-addr-dump.c     Dump address attributes
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-	printf(
-	"Usage: nl-addr-dump <mode> [<filter>]\n"
-	"  mode := { brief | detailed | stats | xml }\n"
-	"  filter := [dev DEV] [label LABEL] [family FAMILY] [scope SCOPE]\n"
-	"            [local ADDR] [peer ADDR] [broadcast ADDR] [anycast ADDR]\n"
-	"            [multicast ADDR]\n");
-	exit(1);
-}
-
-#include "f_addr.c"
-
-int main(int argc, char *argv[])
-{
-	struct nl_handle *nlh;
-	struct nl_cache *link_cache, *addr_cache;
-	struct rtnl_addr *addr;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF
-	};
-	int err = 1;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
-
-	addr = rtnl_addr_alloc();
-	if (!addr)
-		goto errout;
-
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_close;
-
-	addr_cache = nltool_alloc_addr_cache(nlh);
-	if (!addr_cache)
-		goto errout_link_cache;
-
-	params.dp_type = nltool_parse_dumptype(argv[1]);
-	if (params.dp_type < 0)
-		goto errout_addr_cache;
-	get_filter(addr, argc, argv, 2, link_cache);
-
-	nl_cache_dump_filter(addr_cache, &params, (struct nl_object *) addr);
-
-	err = 0;
-
-errout_addr_cache:
-	nl_cache_free(addr_cache);
-errout_link_cache:
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout_free:
-	rtnl_addr_put(addr);
-errout:
-	return err;
-}
diff --git a/src/nl-addr-list.c b/src/nl-addr-list.c
new file mode 100644
index 0000000..9b045a5
--- /dev/null
+++ b/src/nl-addr-list.c
@@ -0,0 +1,193 @@
+/*
+ * src/nl-addr-list.c     List addresses
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation version 2 of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/addr.h>
+#include <netlink/cli/link.h>
+
+static void print_usage(void)
+{
+	printf(
+"Usage: nl-addr-list [OPTION]... [ADDRESS]\n"
+"\n"
+"Options\n"
+"     --details             Show details on multiple lines.\n"
+"     --env                 Print address details in sh env variable syntax.\n"
+"     --prefix=STRING       Prefix each printed line.\n"
+" -h, --help                Show this help.\n"
+" -v, --version             Show versioning information.\n"
+"\n"
+"Address Selection\n"
+" -a, --local=ADDR          Local address.\n"
+" -d, --dev=DEV             Associated network device.\n"
+"     --family=FAMILY       Family of local address.\n"
+"     --label=STRING        Address label (IPv4).\n"
+"     --peer=ADDR           Peer address (IPv4).\n"
+"     --scope=SCOPE         Address scope (IPv4).\n"
+"     --broadcast=ADDR      Broadcast address of network (IPv4).\n"
+"     --valid-lifetime=TS   Valid lifetime before route expires (IPv6).\n"
+"     --preferred=TIME      Preferred lifetime (IPv6).\n"
+"     --valid=TIME          Valid lifetime (IPv6).\n"
+	);
+	exit(0);
+}
+
+static char *prefix;
+
+void print_prefix(struct nl_dump_params *p, int line)
+{
+	if (prefix)
+		nl_dump(p, "%s", prefix);
+}
+
+static void env_dump(struct nl_object *obj, void *arg)
+{
+	struct nl_dump_params *p = arg;
+	struct rtnl_addr *addr = (struct rtnl_addr *) obj;
+	struct nl_cache *link_cache;
+	struct nl_addr *a;
+	static int index = 0;
+	char buf[128], pfx[32], *s;
+
+	snprintf(pfx, sizeof(pfx), "ADDR%d", index++);
+
+	nl_dump_line(p, "%s_FAMILY=%s\n", pfx,
+		     nl_af2str(rtnl_addr_get_family(addr), buf, sizeof(buf)));
+
+	nl_dump_line(p, "%s_LOCAL=%s\n", pfx,
+		     nl_addr2str(rtnl_addr_get_local(addr), buf, sizeof(buf)));
+
+	nl_dump_line(p, "%s_IFINDEX=%u\n", pfx, rtnl_addr_get_ifindex(addr));
+	link_cache = nl_cache_mngt_require("route/link");
+	if (link_cache)
+		nl_dump_line(p, "%s_IFNAME=%s\n", pfx,
+			     rtnl_link_i2name(link_cache,
+			     		      rtnl_addr_get_ifindex(addr),
+			     		      buf, sizeof(buf)));
+
+	if ((a = rtnl_addr_get_peer(addr)))
+		nl_dump_line(p, "%s_PEER=%s\n", pfx,
+			     nl_addr2str(a, buf, sizeof(buf)));
+
+	if ((a = rtnl_addr_get_broadcast(addr)))
+		nl_dump_line(p, "%s_BROADCAST=%s\n", pfx,
+			     nl_addr2str(a, buf, sizeof(buf)));
+
+	nl_dump_line(p, "%s_SCOPE=%s\n", pfx,
+		     rtnl_scope2str(rtnl_addr_get_scope(addr),
+				    buf, sizeof(buf)));
+
+	if ((s = rtnl_addr_get_label(addr)))
+		nl_dump_line(p, "%s_LABEL=%s\n", pfx, s);
+
+	rtnl_addr_flags2str(rtnl_addr_get_flags(addr), buf, sizeof(buf));
+	if (buf[0])
+		nl_dump_line(p, "%s_FLAGS=%s\n", pfx, buf);
+
+	nl_dump_line(p, "%s_CACHEINFO_VALID=%u\n", pfx,
+		     rtnl_addr_get_valid_lifetime(addr));
+
+#if 0
+	if (addr->ce_mask & ADDR_ATTR_CACHEINFO) {
+		struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo;
+
+		nl_dump_line(p, "ADDR_CACHEINFO_PREFERRED=%u\n",
+			     ci->aci_prefered);
+
+		nl_dump_line(p, "ADDR_CACHEINFO_CREATED=%u\n", ci->aci_cstamp);
+		nl_dump_line(p, "ADDR_CACHEINFO_LASTUPDATE=%u\n",
+			     ci->aci_tstamp);
+	}
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct rtnl_addr *addr;
+	struct nl_cache *link_cache, *addr_cache;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_nl_cb = print_prefix,
+		.dp_fd = stdout,
+	};
+	int dump_env = 0;
+
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	addr_cache = nl_cli_addr_alloc_cache(sock);
+	addr = nl_cli_addr_alloc();
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_LABEL = 258,
+			ARG_PEER,
+			ARG_SCOPE,
+			ARG_BROADCAST,
+			ARG_DETAILS,
+			ARG_ENV,
+			ARG_PREFIX,
+			ARG_PREFERRED,
+			ARG_VALID,
+		};
+		static struct option long_opts[] = {
+			{ "details", 0, 0, ARG_DETAILS },
+			{ "env", 0, 0, ARG_ENV },
+			{ "prefix", 1, 0, ARG_PREFIX },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "local", 1, 0, 'a' },
+			{ "dev", 1, 0, 'd' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ "label", 1, 0, ARG_LABEL },
+			{ "peer", 1, 0, ARG_PEER },
+			{ "scope", 1, 0, ARG_SCOPE },
+			{ "broadcast", 1, 0, ARG_BROADCAST },
+			{ "preferred", 1, 0, ARG_PREFERRED },
+			{ "valid", 1, 0, ARG_VALID },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "46hva:d:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case '?': exit(NLE_INVAL);
+		case '4': rtnl_addr_set_family(addr, AF_INET); break;
+		case '6': rtnl_addr_set_family(addr, AF_INET6); break;
+		case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break;
+		case ARG_ENV: dump_env = 1; break;
+		case ARG_PREFIX: prefix = strdup(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'a': nl_cli_addr_parse_local(addr, optarg); break;
+		case 'd': nl_cli_addr_parse_dev(addr, link_cache, optarg); break;
+		case ARG_FAMILY: nl_cli_addr_parse_family(addr, optarg); break;
+		case ARG_LABEL: nl_cli_addr_parse_label(addr, optarg); break;
+		case ARG_PEER: nl_cli_addr_parse_peer(addr, optarg); break;
+		case ARG_SCOPE: nl_cli_addr_parse_scope(addr, optarg); break;
+		case ARG_BROADCAST: nl_cli_addr_parse_broadcast(addr, optarg); break;
+		case ARG_PREFERRED: nl_cli_addr_parse_preferred(addr, optarg); break;
+		case ARG_VALID: nl_cli_addr_parse_valid(addr, optarg); break;
+		}
+	}
+
+	if (dump_env)
+		nl_cache_foreach_filter(addr_cache, OBJ_CAST(addr), env_dump,
+					&params);
+	else
+		nl_cache_dump_filter(addr_cache, &params, OBJ_CAST(addr));
+
+	return 0;
+}
diff --git a/src/nl-cls-add.c b/src/nl-cls-add.c
new file mode 100644
index 0000000..997f02f
--- /dev/null
+++ b/src/nl-cls-add.c
@@ -0,0 +1,117 @@
+/*
+ * src/nl-cls-add.c     Add classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation version 2 of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "cls/utils.h"
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+	printf(
+"Usage: nl-cls-add [OPTION]... [CLASSIFIER] TYPE [TYPE OPTIONS]...\n"
+"\n"
+"Options\n"
+" -q, --quiet               Do not print informal notifications.\n"
+" -h, --help                Show this help.\n"
+" -v, --version             Show versioning information.\n"
+"\n"
+"Classifier Options\n"
+" -d, --dev=DEV             Device the classifier should be assigned to.\n"
+" -p, --parent=HANDLE       Parent QDisc\n"
+"     --proto=PROTO         Protocol (default=IPv4)\n"
+"     --prio=NUM            Priority (0..256)\n"
+"     --id=HANDLE           Unique identifier\n"
+	);
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct rtnl_cls *cls;
+	struct nl_cache *link_cache;
+	struct rtnl_cls_ops *ops;
+	struct cls_module *mod;
+	struct nl_dump_params dp = {
+		.dp_type = NL_DUMP_DETAILS,
+		.dp_fd = stdout,
+	};
+	char *kind;
+	int err, nlflags = NLM_F_CREATE;
+ 
+	sock = nlt_alloc_socket();
+	nlt_connect(sock, NETLINK_ROUTE);
+	link_cache = nlt_alloc_link_cache(sock);
+	cls = nlt_alloc_cls();
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_PROTO = 257,
+			ARG_PRIO = 258,
+			ARG_ID,
+		};
+		static struct option long_opts[] = {
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "dev", 1, 0, 'd' },
+			{ "parent", 1, 0, 'p' },
+			{ "proto", 1, 0, ARG_PROTO },
+			{ "prio", 1, 0, ARG_PRIO },
+			{ "id", 1, 0, ARG_ID },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "+qhva:d:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case '?': exit(NLE_INVAL);
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nlt_print_version(); break;
+		case 'd': parse_dev(cls, link_cache, optarg); break;
+		case 'p': parse_parent(cls, optarg); break;
+		case ARG_PRIO: parse_prio(cls, optarg); break;
+		case ARG_ID: parse_handle(cls, optarg); break;
+		case ARG_PROTO: parse_proto(cls, optarg); break;
+		}
+ 	}
+
+	if (optind >= argc) {
+		print_usage();
+		fatal(EINVAL, "Missing classifier type");
+	}
+
+	kind = argv[optind++];
+	if ((err = rtnl_cls_set_kind(cls, kind)) < 0)
+		fatal(ENOENT, "Unknown classifier type \"%s\".", kind);
+	
+	ops = rtnl_cls_get_ops(cls);
+	if (!(mod = lookup_cls_mod(ops)))
+		fatal(ENOTSUP, "Classifier type \"%s\" not supported.", kind);
+
+	mod->parse_argv(cls, argc, argv);
+
+	printf("Adding ");
+	nl_object_dump(OBJ_CAST(cls), &dp);
+
+	if ((err = rtnl_cls_add(sock, cls, nlflags)) < 0)
+		fatal(err, "Unable to add classifier: %s", nl_geterror(err));
+
+	if (!quiet) {
+		printf("Added ");
+		nl_object_dump(OBJ_CAST(cls), &dp);
+ 	}
+
+	return 0;
+}
diff --git a/src/nl-cls-delete.c b/src/nl-cls-delete.c
new file mode 100644
index 0000000..cfdc170
--- /dev/null
+++ b/src/nl-cls-delete.c
@@ -0,0 +1,133 @@
+/*
+ * src/nl-cls-delete.c     Delete Classifier
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "cls/utils.h"
+
+static int interactive = 0, default_yes = 0, quiet = 0;
+static int deleted = 0;
+static struct nl_sock *sock;
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-cls-list [OPTION]... [CLASSIFIER]\n"
+	"\n"
+	"Options\n"
+	" -i, --interactive     Run interactively\n"
+	"     --yes             Set default answer to yes\n"
+	" -q, --quiet		Do not print informal notifications\n"
+	" -h, --help            Show this help\n"
+	" -v, --version		Show versioning information\n"
+	"\n"
+	"Classifier Options\n"
+	" -d, --dev=DEV         Device the classifier should be assigned to.\n"
+	" -p, --parent=HANDLE   Parent qdisc/class\n"
+	"     --proto=PROTO     Protocol\n"
+	"     --prio=NUM        Priority (0..256)\n"
+	"     --id=HANDLE       Unique identifier\n"
+	);
+	exit(0);
+}
+
+static void delete_cb(struct nl_object *obj, void *arg)
+{
+	struct rtnl_cls *cls = (struct rtnl_cls *) obj;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+	int err;
+
+	if (interactive && !nlt_confirm(obj, &params, default_yes))
+		return;
+
+	if ((err = rtnl_cls_delete(sock, cls, 0)) < 0)
+		fatal(err, "Unable to delete classifier: %s",
+		      nl_geterror(err));
+
+	if (!quiet) {
+		printf("Deleted ");
+		nl_object_dump(obj, &params);
+	}
+
+	deleted++;
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_cache *link_cache, *cls_cache;
+	struct rtnl_cls *cls;
+	int nf = 0, err;
+
+	sock = nlt_alloc_socket();
+	nlt_connect(sock, NETLINK_ROUTE);
+	link_cache = nlt_alloc_link_cache(sock);
+	cls = nlt_alloc_cls();
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_PRIO = 257,
+			ARG_PROTO = 258,
+			ARG_ID,
+			ARG_YES,
+		};
+		static struct option long_opts[] = {
+			{ "interactive", 0, 0, 'i' },
+			{ "yes", 0, 0, ARG_YES },
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "dev", 1, 0, 'd' },
+			{ "parent", 1, 0, 'p' },
+			{ "proto", 1, 0, ARG_PROTO },
+			{ "prio", 1, 0, ARG_PRIO },
+			{ "id", 1, 0, ARG_ID },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "iqhvd:p:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'i': interactive = 1; break;
+		case ARG_YES: default_yes = 1; break;
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nlt_print_version(); break;
+		case 'd': nf++; parse_dev(cls, link_cache, optarg); break;
+		case 'p': nf++; parse_parent(cls, optarg); break;
+		case ARG_PRIO: nf++; parse_prio(cls, optarg); break;
+		case ARG_ID: nf++; parse_handle(cls, optarg); break;
+		case ARG_PROTO: nf++; parse_proto(cls, optarg); break;
+		}
+	}
+
+	if (nf == 0 && !interactive && !default_yes) {
+		fprintf(stderr, "You attempted to delete all classifiers in "
+			"non-interactive mode, aborting.\n");
+		exit(0);
+	}
+
+	err = rtnl_cls_alloc_cache(sock, rtnl_cls_get_ifindex(cls),
+				   rtnl_cls_get_parent(cls), &cls_cache);
+	if (err < 0)
+		fatal(err, "Unable to allocate classifier cache: %s",
+		      nl_geterror(err));
+
+	nl_cache_foreach_filter(cls_cache, OBJ_CAST(cls), delete_cb, NULL);
+
+	if (!quiet)
+		printf("Deleted %d classifiers\n", deleted);
+
+	return 0;
+}
diff --git a/src/nl-cls-list.c b/src/nl-cls-list.c
new file mode 100644
index 0000000..9121d52
--- /dev/null
+++ b/src/nl-cls-list.c
@@ -0,0 +1,113 @@
+/*
+ * src/nl-cls-list.c     	List classifiers
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "cls/utils.h"
+
+static struct nl_sock *sock;
+static struct rtnl_cls *cls;
+static struct nl_dump_params params = {
+	.dp_type = NL_DUMP_LINE,
+};
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-cls-list [OPTION]... [CLASSIFIER]\n"
+	"\n"
+	"Options\n"
+	" -f, --format=TYPE     Output format { brief | details | stats }\n"
+	" -h, --help            Show this help text.\n"
+	" -v, --version         Show versioning information.\n"
+	"\n"
+	"Classifier Options\n"
+	" -d, --dev=DEV         Device the classifier should be assigned to.\n"
+	" -p, --parent=HANDLE   Parent qdisc/class\n"
+	"     --proto=PROTO     Protocol\n"
+	"     --prio=NUM        Priority\n"
+	"     --id=NUM          Identifier\n"
+	);
+	exit(0);
+}
+
+static void print_cls(struct nl_object *obj, void *arg)
+{
+	struct nl_cache *cls_cache;
+	int err, ifindex;
+
+	if (obj)
+		ifindex = rtnl_link_get_ifindex((struct rtnl_link *) obj);
+	else
+		ifindex = rtnl_cls_get_ifindex(cls);
+
+	err = rtnl_cls_alloc_cache(sock, ifindex, rtnl_cls_get_parent(cls),
+				   &cls_cache);
+	if (err < 0)
+		fatal(err, "Unable to allocate classifier cache: %s",
+		      nl_geterror(err));
+
+	nl_cache_dump_filter(cls_cache, &params, OBJ_CAST(cls));
+	nl_cache_free(cls_cache);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_cache *link_cache;
+	int dev = 0;
+
+	params.dp_fd = stdout;
+	sock = nlt_alloc_socket();
+	nlt_connect(sock, NETLINK_ROUTE);
+	link_cache = nlt_alloc_link_cache(sock);
+	cls = nlt_alloc_cls();
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_PROTO = 257,
+			ARG_PRIO = 258,
+			ARG_ID,
+		};
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "dev", 1, 0, 'd' },
+			{ "parent", 1, 0, 'p' },
+			{ "proto", 1, 0, ARG_PROTO },
+			{ "prio", 1, 0, ARG_PRIO },
+			{ "id", 1, 0, ARG_ID },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "+f:qhva:d:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case '?': exit(NLE_INVAL);
+		case 'f': params.dp_type = nlt_parse_dumptype(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nlt_print_version(); break;
+		case 'd': dev = 1; parse_dev(cls, link_cache, optarg); break;
+		case 'p': parse_parent(cls, optarg); break;
+		case ARG_PRIO: parse_prio(cls, optarg); break;
+		case ARG_ID: parse_handle(cls, optarg); break;
+		case ARG_PROTO: parse_proto(cls, optarg); break;
+		}
+ 	}
+
+	if (!dev)
+		nl_cache_foreach(link_cache, print_cls, NULL);
+	else
+		print_cls(NULL, NULL);
+
+	return 0;
+}
diff --git a/src/nl-fib-lookup.c b/src/nl-fib-lookup.c
index 5bbf91e..705cf32 100644
--- a/src/nl-fib-lookup.c
+++ b/src/nl-fib-lookup.c
@@ -6,10 +6,10 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
 
 static void print_usage(void)
 {
@@ -25,21 +25,18 @@
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
+	struct nl_sock *nlh;
 	struct nl_cache *result;
 	struct flnl_request *request;
 	struct nl_addr *addr;
 	struct nl_dump_params params = {
 		.dp_fd = stdout,
-		.dp_type = NL_DUMP_FULL,
+		.dp_type = NL_DUMP_DETAILS,
 	};
 	int table = RT_TABLE_UNSPEC, scope = RT_SCOPE_UNIVERSE;
 	int tos = 0, err = 1;
 	uint64_t fwmark = 0;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
 	while (1) {
 		static struct option long_opts[] = {
 			{"table", 1, 0, 't'},
@@ -76,24 +73,19 @@
 	if (optind >= argc)
 		print_usage();
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
+	nlh = nl_cli_alloc_socket();
 
-	addr = nl_addr_parse(argv[optind], AF_INET);
-	if (!addr) {
-		fprintf(stderr, "Unable to parse address \"%s\": %s\n",
-			argv[optind], nl_geterror());
-		goto errout;
-	}
+	if ((err = nl_addr_parse(argv[optind], AF_INET, &addr)) < 0)
+		nl_cli_fatal(err, "Unable to parse address \"%s\": %s\n",
+			argv[optind], nl_geterror(err));
 
 	result = flnl_result_alloc_cache();
 	if (!result)
-		goto errout_addr;
+		nl_cli_fatal(ENOMEM, "Unable to allocate cache");
 
 	request = flnl_request_alloc();
 	if (!request)
-		goto errout_result;
+		nl_cli_fatal(ENOMEM, "Unable to allocate request");
 
 	flnl_request_set_table(request, table);
 	flnl_request_set_fwmark(request, fwmark);
@@ -103,28 +95,15 @@
 	err = flnl_request_set_addr(request, addr);
 	nl_addr_put(addr);
 	if (err < 0)
-		goto errout_put;
+		nl_cli_fatal(err, "Unable to send request: %s", nl_geterror(err));
 
-	if (nltool_connect(nlh, NETLINK_FIB_LOOKUP) < 0)
-		goto errout_put;
+	nl_cli_connect(nlh, NETLINK_FIB_LOOKUP);
 
 	err = flnl_lookup(nlh, request, result);
-	if (err < 0) {
-		fprintf(stderr, "Unable to lookup: %s\n", nl_geterror());
-		goto errout_put;
-	}
+	if (err < 0)
+		nl_cli_fatal(err, "Unable to lookup: %s\n", nl_geterror(err));
 
 	nl_cache_dump(result, &params);
 
-	err = 0;
-errout_put:
-	nl_object_put(OBJ_CAST(request));
-errout_result:
-	nl_cache_free(result);
-errout_addr:
-	nl_addr_put(addr);
-errout:
-	nl_close(nlh);
-	nl_handle_destroy(nlh);
-	return err;
+	return 0;
 }
diff --git a/src/nl-link-dump.c b/src/nl-link-dump.c
deleted file mode 100644
index 0214025..0000000
--- a/src/nl-link-dump.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * src/nl-link-dump.c	Dump link attributes
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-	printf(
-	"Usage: nl-link-dump <mode> [<filter>]\n"
-	"  mode := { brief | detailed | stats | xml }\n"
-	"  filter := [dev DEV] [mtu MTU] [txqlen TXQLEN] [weight WEIGHT] [link LINK]\n"
-	"            [master MASTER] [qdisc QDISC] [addr ADDR] [broadcast BRD]\n"
-	"            [{ up | down }] [{ arp | noarp }] [{ promisc | nopromisc }]\n"
-	"            [{ dynamic | nodynamic }] [{ multicast | nomulticast }]\n"
-	"            [{ trailers | notrailers }] [{ allmulticast | noallmulticast }]\n");
-	exit(1);
-}
-
-#include "f_link.c"
-
-int main(int argc, char *argv[])
-{
-	struct nl_handle *nlh;
-	struct nl_cache *link_cache;
-	struct rtnl_link *link;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF
-	};
-	int err = 1;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
-
-	link = rtnl_link_alloc();
-	if (!link)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_put;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_put;
-
-	params.dp_type = nltool_parse_dumptype(argv[1]);
-	if (params.dp_type < 0)
-		goto errout_put;
-
-	get_filter(link, argc, argv, 2, link_cache);
-	nl_cache_dump_filter(link_cache, &params, (struct nl_object *) link);
-	nl_cache_free(link_cache);
-	err = 0;
-errout_put:
-	rtnl_link_put(link);
-errout:
-	nl_close(nlh);
-	nl_handle_destroy(nlh);
-	return err;
-}
diff --git a/src/nl-link-ifindex2name.c b/src/nl-link-ifindex2name.c
index e1043fb..68e5158 100644
--- a/src/nl-link-ifindex2name.c
+++ b/src/nl-link-ifindex2name.c
@@ -6,50 +6,39 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
 
-int main(int argc, char **argv)
+static void print_usage(void)
 {
-	struct nl_handle *nlh;
+	printf("Usage: nl-link-ifindex2name <ifindex>\n");
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
 	struct nl_cache *link_cache;
-	int err = -1, ifindex;
-	char dst[32] = {0};
-	const char *name;
+	char name[IFNAMSIZ];
+	uint32_t ifindex;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
+	if (argc < 2)
+		print_usage();
 
-	if (argc < 2 || !strcmp(argv[1], "-h")) {
-		fprintf(stderr, "Usage: nl-link-ifindex2name <ifindex>\n");
-		return -1;
-	}
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
+	ifindex = nl_cli_parse_u32(argv[1]);
 
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout;
+	if (!rtnl_link_i2name(link_cache, ifindex, name, sizeof(name)))
+		nl_cli_fatal(ENOENT, "Interface index %d does not exist",
+			     ifindex);
 
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout;
+	printf("%s\n", name);
 
-	ifindex = strtoul(argv[1], NULL, 0);
-
-	if (!(name = rtnl_link_i2name(link_cache, ifindex, dst, sizeof(dst))))
-		fprintf(stderr, "Interface index %d does not exist\n", ifindex);
-	else
-		printf("%s\n", name);
-
-	nl_cache_free(link_cache);
-	err = 0;
-errout:
-	nl_close(nlh);
-	nl_handle_destroy(nlh);
-
-	return err;
+	return 0;
 }
diff --git a/src/nl-link-list.c b/src/nl-link-list.c
new file mode 100644
index 0000000..5e1e3f6
--- /dev/null
+++ b/src/nl-link-list.c
@@ -0,0 +1,106 @@
+/*
+ * src/nl-link-dump.c	Dump link attributes
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#if 0
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-link-dump <mode> [<filter>]\n"
+	"  mode := { brief | detailed | stats | xml }\n"
+	"  filter := [dev DEV] [mtu MTU] [txqlen TXQLEN] [weight WEIGHT] [link LINK]\n"
+	"            [master MASTER] [qdisc QDISC] [addr ADDR] [broadcast BRD]\n"
+	"            [{ up | down }] [{ arp | noarp }] [{ promisc | nopromisc }]\n"
+	"            [{ dynamic | nodynamic }] [{ multicast | nomulticast }]\n"
+	"            [{ trailers | notrailers }] [{ allmulticast | noallmulticast }]\n");
+	exit(1);
+}
+#endif
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-link-list [OPTION]... [Link]\n"
+	"\n"
+	"Options\n"
+	" -f, --format=TYPE     Output format { brief | details | stats }\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	"\n"
+	"Link Options\n"
+	" -n, --name=NAME	link name\n"
+	" -i, --index           interface index\n"
+	"     --mtu=NUM         MTU value\n"
+	"     --txqlen=NUM      TX queue length\n"
+	"     --weight=NUM      weight\n"
+	);
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct nl_cache *link_cache;
+	struct rtnl_link *link;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	link = nl_cli_link_alloc();
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_MTU = 258,
+			ARG_TXQLEN,
+			ARG_WEIGHT,
+		};
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "name", 1, 0, 'n' },
+			{ "index", 1, 0, 'i' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ "mtu", 1, 0, ARG_MTU },
+			{ "txqlen", 1, 0, ARG_TXQLEN },
+			{ "weight", 1, 0, ARG_WEIGHT },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "f:hvn:i:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'n': nl_cli_link_parse_name(link, optarg); break;
+		case 'i': nl_cli_link_parse_ifindex(link, optarg); break;
+		case ARG_FAMILY: nl_cli_link_parse_family(link, optarg); break;
+		case ARG_MTU: nl_cli_link_parse_mtu(link, optarg); break;
+		case ARG_TXQLEN: nl_cli_link_parse_txqlen(link, optarg); break;
+		case ARG_WEIGHT: nl_cli_link_parse_weight(link, optarg); break;
+		}
+	}
+
+	nl_cache_dump_filter(link_cache, &params, OBJ_CAST(link));
+
+	return 0;
+}
diff --git a/src/nl-link-name2ifindex.c b/src/nl-link-name2ifindex.c
index 993397d..b04af04 100644
--- a/src/nl-link-name2ifindex.c
+++ b/src/nl-link-name2ifindex.c
@@ -6,46 +6,36 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
+
+static void print_usage(void)
+{
+	printf("Usage: nl-link-ifindex2name <ifindex>\n");
+	exit(0);
+}
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
+	struct nl_sock *sock;
 	struct nl_cache *link_cache;
-	int err = -1, ifindex;
+	uint32_t ifindex;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
+	if (argc < 2)
+		print_usage();
 
-	if (argc < 2 || !strcmp(argv[1], "-h")) {
-		printf("Usage: nl-link-name2ifindex <name>\n");
-		return -1;
-	}
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
+	if (!(ifindex = rtnl_link_name2i(link_cache, argv[1])))
+		nl_cli_fatal(ENOENT, "Interface \"%s\" does not exist",
+			     argv[1]);
 
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout;
+	printf("%u\n", ifindex);
 
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout;
-
-	if ((ifindex = rtnl_link_name2i(link_cache, argv[1])) == RTNL_LINK_NOT_FOUND)
-		fprintf(stderr, "Interface %s does not exist\n", argv[1]);
-	else
-		printf("%d\n", ifindex);
-
-	nl_cache_free(link_cache);
-	err = 0;
-errout:
-	nl_close(nlh);
-	nl_handle_destroy(nlh);
-
-	return err;
+	return 0;
 }
diff --git a/src/nl-link-set.c b/src/nl-link-set.c
index 1872301..94c94e7 100644
--- a/src/nl-link-set.c
+++ b/src/nl-link-set.c
@@ -6,78 +6,119 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
 
-static void print_usage(void)
-{
-	printf(
-	"Usage: nl-link-set <ifindex> <changes>\n"
-	"  changes := [dev DEV] [mtu MTU] [txqlen TXQLEN] [weight WEIGHT] [link LINK]\n"
+static struct nl_sock *sock;
+static int quiet = 0;
+
+#if 0
+	"  changes := [link LINK]\n"
 	"             [master MASTER] [qdisc QDISC] [addr ADDR] [broadcast BRD]\n"
 	"             [{ up | down }] [{ arp | noarp }] [{ promisc | nopromisc }]\n"
 	"             [{ dynamic | nodynamic }] [{ multicast | nomulticast }]\n"
 	"             [{ trailers | notrailers }] [{ allmulticast | noallmulticast }]\n");
-	exit(1);
+#endif
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-link-set [OPTION]... [LINK]\n"
+	"\n"
+	"Options\n"
+	" -q, --quiet		Do not print informal notifications\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	"\n"
+	"Selecting the Link\n"
+	" -n, --name=NAME	link name\n"
+	" -i, --index           interface index\n"
+	"Change Options\n"
+	"     --rename=NAME     rename interface\n"
+	"     --mtu=NUM         MTU value\n"
+	"     --txqlen=NUM      TX queue length\n"
+	"     --weight=NUM      weight\n"
+	);
+	exit(0);
 }
 
-#include "f_link.c"
+static void set_cb(struct nl_object *obj, void *arg)
+{
+	struct rtnl_link *link = nl_object_priv(obj);
+	struct rtnl_link *change = arg;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+	int err;
+
+	if ((err = rtnl_link_change(sock, link, change, 0) < 0))
+		nl_cli_fatal(err, "Unable to change link: %s",
+			     nl_geterror(err));
+
+	if (!quiet) {
+		printf("Changed ");
+		nl_object_dump(OBJ_CAST(link), &params);
+	}
+}
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
 	struct nl_cache *link_cache;
-	struct rtnl_link *link, *orig;
-	int err = 1, ifindex;
+	struct rtnl_link *link, *change;
+	int ok = 0;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	link = nl_cli_link_alloc();
+	change = nl_cli_link_alloc();
 
-	if (argc < 2 || !strcmp(argv[1], "-h"))
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_RENAME = 257,
+			ARG_MTU = 258,
+			ARG_TXQLEN,
+			ARG_WEIGHT,
+		};
+		static struct option long_opts[] = {
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "name", 1, 0, 'n' },
+			{ "index", 1, 0, 'i' },
+			{ "rename", 1, 0, ARG_RENAME },
+			{ "mtu", 1, 0, ARG_MTU },
+			{ "txqlen", 1, 0, ARG_TXQLEN },
+			{ "weight", 1, 0, ARG_WEIGHT },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "qhvn:i:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'n': ok++; nl_cli_link_parse_name(link, optarg); break;
+		case 'i': ok++; nl_cli_link_parse_ifindex(link, optarg); break;
+		case ARG_RENAME: nl_cli_link_parse_name(change, optarg); break;
+		case ARG_MTU: nl_cli_link_parse_mtu(link, optarg); break;
+		case ARG_TXQLEN: nl_cli_link_parse_txqlen(link, optarg); break;
+		case ARG_WEIGHT: nl_cli_link_parse_weight(link, optarg); break;
+		}
+	}
+
+	if (!ok)
 		print_usage();
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
+	nl_cache_foreach_filter(link_cache, OBJ_CAST(link), set_cb, change);
 
-	link = rtnl_link_alloc();
-	if (!link)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_close;
-
-	ifindex = strtoul(argv[1], NULL, 0);
-
-	if (!(orig = rtnl_link_get(link_cache, ifindex))) {
-		fprintf(stderr, "Interface index %d does not exist\n", ifindex);
-		goto errout_cache;
-	}
-
-	get_filter(link, argc, argv, 2, link_cache);
-
-	if (rtnl_link_change(nlh, orig, link, 0) < 0) {
-		fprintf(stderr, "Unable to change link: %s\n", nl_geterror());
-		goto errout_put;
-	}
-
-	err = 0;
-
-errout_put:
-	rtnl_link_put(orig);
-errout_cache:
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout_free:
-	rtnl_link_put(link);
-errout:
-	nl_handle_destroy(nlh);
-	return err;
+	return 0;
 }
diff --git a/src/nl-link-stats.c b/src/nl-link-stats.c
index 8a9200e..4dfca86 100644
--- a/src/nl-link-stats.c
+++ b/src/nl-link-stats.c
@@ -6,31 +6,46 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
 
 static void print_usage(void)
 {
 	printf(
-"Usage: nl-link-stats <ifindex> [<statistic> ...]\n"
-"  ifindex   := { all | number }\n"
-"  statistic := { (rx|tx)_packets | (rx|tx)_bytes | (rx|tx)_errors |\n"
-"                 (rx|tx)_dropped | (rx|tx)_compressed | (rx|tx)_fifo_err |\n" \
-"                 rx_len_err | rx_over_err | rx_crc_err | rx_frame_err |\n"
-"                 rx_missed_err | tx_abort_err | tx_carrier_err |\n"
-"                 tx_hbeat_err | tx_win_err | tx_collision | multicast }\n");
-	exit(1);
+	"Usage: nl-link-stats [OPTION]... [LINK] [ListOfStats]\n"
+	"\n"
+	"Options\n"
+	" -l, --list            List available statistic names\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	"\n"
+	"Link Options\n"
+	" -n, --name=NAME	link name\n"
+	" -i, --index=NUM       interface index\n"
+	);
+	exit(0);
 }
 
-static char **gargv;
+static void list_stat_names(void)
+{
+	char buf[64];
+	int i;
+
+	for (i = 0; i < RTNL_LINK_STATS_MAX; i++)
+		printf("%s\n", rtnl_link_stat2str(i, buf, sizeof(buf)));
+
+	exit(0);
+}
+
 static int gargc;
 
 static void dump_stat(struct rtnl_link *link, int id)
 {
 	uint64_t st = rtnl_link_get_stat(link, id);
-	char buf[62];
+	char buf[64];
 
 	printf("%s.%s %" PRIu64 "\n", rtnl_link_get_name(link),
 	       rtnl_link_stat2str(id, buf, sizeof(buf)), st);
@@ -38,72 +53,67 @@
 
 static void dump_stats(struct nl_object *obj, void *arg)
 {
-	int i;
 	struct rtnl_link *link = (struct rtnl_link *) obj;
+	char **argv = arg;
 
-	if (!strcasecmp(gargv[0], "all")) {
+	if (optind >= gargc) {
+		int i;
+
 		for (i = 0; i < RTNL_LINK_STATS_MAX; i++)
 			dump_stat(link, i);
 	} else {
-		for (i = 0; i < gargc; i++) {
-			int id = rtnl_link_str2stat(gargv[i]);
+		while (optind < gargc) {
+			int id = rtnl_link_str2stat(argv[optind]);
 
 			if (id < 0)
 				fprintf(stderr, "Warning: Unknown statistic "
-					"\"%s\"\n", gargv[i]);
+					"\"%s\"\n", argv[optind]);
 			else
 				dump_stat(link, id);
+
+			optind++;
 		}
 	}
 }
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
+	struct nl_sock *sock;
 	struct nl_cache *link_cache;
-	int err = 1;
+	struct rtnl_link *link;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	link = nl_cli_link_alloc();
 
-	if (argc < 3 || !strcmp(argv[1], "-h"))
-		print_usage();
+	for (;;) {
+		int c, optidx = 0;
+		static struct option long_opts[] = {
+			{ "list", 0, 0, 'l' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "name", 1, 0, 'n' },
+			{ "index", 1, 0, 'i' },
+			{ 0, 0, 0, 0 }
+		};
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
+		c = getopt_long(argc, argv, "lhvn:i:", long_opts, &optidx);
+		if (c == -1)
+			break;
 
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_close;
-
-	gargv = &argv[2];
-	gargc = argc - 2;
-
-	if (!strcasecmp(argv[1], "all"))
-		nl_cache_foreach(link_cache, dump_stats, NULL);
-	else {
-		int ifindex = strtoul(argv[1], NULL, 0);
-		struct rtnl_link *link = rtnl_link_get(link_cache, ifindex);
-
-		if (!link) {
-			fprintf(stderr, "Could not find ifindex %d\n", ifindex);
-			goto errout_link_cache;
+		switch (c) {
+		case 'l': list_stat_names(); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'n': nl_cli_link_parse_name(link, optarg); break;
+		case 'i': nl_cli_link_parse_ifindex(link, optarg); break;
 		}
-
-		dump_stats((struct nl_object *) link, NULL);
-		rtnl_link_put(link);
 	}
 
-	err = 0;
-errout_link_cache:
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout:
-	nl_handle_destroy(nlh);
-	return err;
+	gargc = argc;
+	nl_cache_foreach_filter(link_cache, OBJ_CAST(link), dump_stats, argv);
+
+	return 0;
 }
+
diff --git a/src/nl-list-caches.c b/src/nl-list-caches.c
index ed0e92a..7e4ffc1 100644
--- a/src/nl-list-caches.c
+++ b/src/nl-list-caches.c
@@ -6,10 +6,11 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink-local.h>
+#include <netlink/cli/utils.h>
 
 static void print_usage(void)
 {
@@ -47,9 +48,7 @@
 			"brief",
 			"detailed",
 			"stats",
-			"xml",
 			"env",
-			"events"
 		};
 		int i;
 
diff --git a/src/nl-list-sockets.c b/src/nl-list-sockets.c
index 9a1333c..868006e 100644
--- a/src/nl-list-sockets.c
+++ b/src/nl-list-sockets.c
@@ -6,35 +6,26 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
 
 #define PROC_NETLINK "/proc/net/netlink"
 
-static void print_usage(void)
-{
-	fprintf(stderr, "Usage: nl-list-sockets [<file>]\n");
-	exit(1);
-}
-
 int main(int argc, char *argv[])
 {
 	FILE *fd;
 	char buf[2048], p[64];
 
-	if (argc > 1 && !strcasecmp(argv[1], "-h"))
-		print_usage();
-
 	fd = fopen(PROC_NETLINK, "r");
 	if (fd == NULL) {
 		perror("fopen");
 		return -1;
 	}
 
-	printf("Address    Family           PID    Groups   rmem   wmem   " \
-	       "CB         refcnt\n");
+	printf("Address            Family           PID    Groups   rmem   "
+	       "wmem   CB         refcnt\n");
 
 	while (fgets(buf, sizeof(buf), fd)) {
 		unsigned long sk, cb;
@@ -47,7 +38,7 @@
 		if (ret != 8)
 			continue;
 		
-		printf("0x%08lx %-16s %-6d %08x %-6d %-6d 0x%08lx %d\n",
+		printf("0x%016lx %-16s %-6d %08x %-6d %-6d 0x%08lx %d\n",
 			sk, nl_nlfamily2str(proto, p, sizeof(p)), pid,
 			groups, rmem, wmem, cb, refcnt);
 	}
diff --git a/src/nl-monitor.c b/src/nl-monitor.c
index 9f21dbb..fdf6497 100644
--- a/src/nl-monitor.c
+++ b/src/nl-monitor.c
@@ -6,12 +6,11 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
-#include <netlink/route/link.h>
-#include <netlink/route/addr.h>
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
 
 static void obj_input(struct nl_object *obj, void *arg)
 {
@@ -35,7 +34,7 @@
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
+	struct nl_sock *sock;
 	struct nl_cache *link_cache;
 	int err = 1;
 	int i, idx;
@@ -61,16 +60,9 @@
 		{ RTNLGRP_NONE, NULL }
 	};
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	nlh = nltool_alloc_handle();
-	if (nlh == NULL)
-		return -1;
-
-	nl_disable_sequence_check(nlh);
-
-	nl_socket_modify_cb(nlh, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+	sock = nl_cli_alloc_socket();
+	nl_socket_disable_seq_check(sock);
+	nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
 
 	if (argc > 1 && !strcasecmp(argv[1], "-h")) {
 		printf("Usage: nl-monitor [<groups>]\n");
@@ -82,18 +74,15 @@
 		return 2;
 	}
 
-	if (nl_connect(nlh, NETLINK_ROUTE) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout;
-	}
+	nl_cli_connect(sock, NETLINK_ROUTE);
 
 	for (idx = 1; argc > idx; idx++) {
 		for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++) {
 			if (!strcmp(argv[idx], known_groups[i].gr_name)) {
 
-				if (nl_socket_add_membership(nlh, known_groups[i].gr_id) < 0) {
-					fprintf(stderr, "%s: %s\n", argv[idx], nl_geterror());
-					goto errout;
+				if ((err = nl_socket_add_membership(sock, known_groups[i].gr_id)) < 0) {
+					nl_cli_fatal(err, "%s: %s\n", argv[idx],
+						     nl_geterror(err));
 				}
 
 				break;
@@ -103,18 +92,13 @@
 			fprintf(stderr, "Warning: Unknown group: %s\n", argv[idx]);
 	}
 
-	if ((link_cache = rtnl_link_alloc_cache(nlh)) == NULL) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout_close;
-	}
-
-	nl_cache_mngt_provide(link_cache);
+	link_cache = nl_cli_link_alloc_cache(sock);
 
 	while (1) {
 		fd_set rfds;
 		int fd, retval;
 
-		fd = nl_socket_get_fd(nlh);
+		fd = nl_socket_get_fd(sock);
 
 		FD_ZERO(&rfds);
 		FD_SET(fd, &rfds);
@@ -123,13 +107,9 @@
 
 		if (retval) {
 			/* FD_ISSET(fd, &rfds) will be true */
-			nl_recvmsgs_default(nlh);
+			nl_recvmsgs_default(sock);
 		}
 	}
 
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout:
-	return err;
+	return 0;
 }
diff --git a/src/nl-neigh-add.c b/src/nl-neigh-add.c
index 14b99de..4cddabe 100644
--- a/src/nl-neigh-add.c
+++ b/src/nl-neigh-add.c
@@ -6,74 +6,105 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/neigh.h>
+#include <netlink/cli/link.h>
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-neigh-add [OPTION]... NEIGHBOUR\n"
+	"\n"
+	"Options\n"
+	"     --update-only     Do not create neighbour, updates exclusively\n"
+	"     --create-only     Do not update neighbour if it exists already.\n"
+	" -q, --quiet           Do not print informal notifications\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	"\n"
+	"Neighbour Options\n"
+	" -a, --addr=ADDR       Destination address of neighbour\n"
+	" -l, --lladdr=ADDR     Link layer address of neighbour\n"
+	" -d, --dev=DEV         Device the neighbour is connected to\n"
+	"     --state=STATE     Neighbour state, (default = permanent)\n"
+	"\n"
+	"Example\n"
+	"  nl-neigh-add --create-only --addr=10.0.0.1 --dev=eth0 \\\n"
+	"               --lladdr=AA:BB:CC:DD:EE:FF\n"
+	);
+
+	exit(0);
+}
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
+	struct nl_sock *sock;
 	struct rtnl_neigh *neigh;
-	struct nl_addr *addr;
-	int err = 1;
+	struct nl_cache *link_cache;
+	struct nl_dump_params dp = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+	int err, ok = 0, nlflags = NLM_F_REPLACE | NLM_F_CREATE;
+ 
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+ 	neigh = nl_cli_neigh_alloc();
+ 
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_UPDATE_ONLY = 257,
+			ARG_CREATE_ONLY = 258,
+			ARG_STATE,
+		};
+		static struct option long_opts[] = {
+			{ "update-only", 0, 0, ARG_UPDATE_ONLY },
+			{ "create-only", 0, 0, ARG_CREATE_ONLY },
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "addr", 1, 0, 'a' },
+			{ "lladdr", 1, 0, 'l' },
+			{ "dev", 1, 0, 'd' },
+			{ "state", 1, 0, ARG_STATE },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "qhva:l:d:", long_opts, &optidx);
+		if (c == -1)
+			break;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 4 || !strcmp(argv[1], "-h")) {
-		printf("Usage: nl-neigh-add <addr> <lladdr> "
-		       "<ifindex> [<state>]\n");
-		return 1;
-	}
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
-
-	neigh = rtnl_neigh_alloc();
-	if (!neigh)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free;
-
-	addr = nltool_addr_parse(argv[1]);
-	if (!addr)
-		goto errout_close;
-	rtnl_neigh_set_dst(neigh, addr);
-	nl_addr_put(addr);
-
-	addr = nltool_addr_parse(argv[2]);
-	if (!addr)
-		goto errout_close;
-	rtnl_neigh_set_lladdr(neigh, addr);
-	nl_addr_put(addr);
-
-	rtnl_neigh_set_ifindex(neigh, strtoul(argv[3], NULL, 0));
-
-	if (argc > 4) {
-		int state = rtnl_neigh_str2state(argv[4]);
-		if (state < 0) {
-			fprintf(stderr, "Unknown state \"%s\"\n", argv[4]);
-			goto errout_close;
+		switch (c) {
+		case ARG_UPDATE_ONLY: nlflags &= ~NLM_F_CREATE; break;
+		case ARG_CREATE_ONLY: nlflags |= NLM_F_EXCL; break;
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'a': ok++; nl_cli_neigh_parse_dst(neigh, optarg); break;
+		case 'l': nl_cli_neigh_parse_lladdr(neigh, optarg); break;
+		case 'd': nl_cli_neigh_parse_dev(neigh, link_cache, optarg); break;
+		case ARG_STATE: nl_cli_neigh_parse_state(neigh, optarg); break;
 		}
-		rtnl_neigh_set_state(neigh, state);
-	} else
-		rtnl_neigh_set_state(neigh, NUD_PERMANENT);
+ 	}
 
-	if (rtnl_neigh_add(nlh, neigh, 0) < 0) {
-		fprintf(stderr, "Unable to add address: %s\n", nl_geterror());
-		goto errout_close;
-	}
+	if (!ok)
+		print_usage();
 
-	err = 0;
+	if ((err = rtnl_neigh_add(sock, neigh, nlflags)) < 0)
+		nl_cli_fatal(err, "Unable to add neighbour: %s",
+			     nl_geterror(err));
 
-errout_close:
-	nl_close(nlh);
-errout_free:
-	rtnl_neigh_put(neigh);
-errout:
-	nl_handle_destroy(nlh);
-	return err;
+	if (!quiet) {
+		printf("Added ");
+		nl_object_dump(OBJ_CAST(neigh), &dp);
+ 	}
+
+	return 0;
 }
diff --git a/src/nl-neigh-delete.c b/src/nl-neigh-delete.c
index 7829d34..887bd84 100644
--- a/src/nl-neigh-delete.c
+++ b/src/nl-neigh-delete.c
@@ -6,61 +6,117 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/neigh.h>
+#include <netlink/cli/link.h>
+
+static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
+struct nl_sock *sock;
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-neigh-delete [OPTION]... [NEIGHBOUR]\n"
+	"\n"
+	"Options\n"
+	" -i, --interactive     Run interactively\n"
+	"     --yes             Set default answer to yes\n"
+	" -q, --quiet           Do not print informal notifications\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	"\n"
+	"Neighbour Options\n"
+	" -a, --addr=ADDR       Destination address of neighbour\n"
+	" -l, --lladdr=ADDR     Link layer address of neighbour\n"
+	" -d, --dev=DEV         Device the neighbour is connected to\n"
+	"     --family=FAMILY   Destination address family\n"
+	"     --state=STATE     Neighbour state, (default = permanent)\n"
+	);
+
+	exit(0);
+}
+
+static void delete_cb(struct nl_object *obj, void *arg)
+{
+	struct rtnl_neigh *neigh = nl_object_priv(obj);
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+	int err;
+
+	if (interactive && !nl_cli_confirm(obj, &params, default_yes))
+		return;
+
+	if ((err = rtnl_neigh_delete(sock, neigh, 0)) < 0)
+		nl_cli_fatal(err, "Unable to delete neighbour: %s\n",
+			     nl_geterror(err));
+
+	if (!quiet) {
+		printf("Deleted ");
+		nl_object_dump(obj, &params);
+	}
+
+	deleted++;
+}
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
 	struct rtnl_neigh *neigh;
-	struct nl_addr *addr;
-	int err = 1;
+	struct nl_cache *link_cache, *neigh_cache;
+ 
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	neigh_cache = nl_cli_neigh_alloc_cache(sock);
+ 	neigh = nl_cli_neigh_alloc();
+ 
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_STATE = 258,
+			ARG_YES,
+		};
+		static struct option long_opts[] = {
+			{ "interactive", 0, 0, 'i' },
+			{ "yes", 0, 0, ARG_YES },
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "addr", 1, 0, 'a' },
+			{ "lladdr", 1, 0, 'l' },
+			{ "dev", 1, 0, 'd' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ "state", 1, 0, ARG_STATE },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "qhva:l:d:", long_opts, &optidx);
+		if (c == -1)
+			break;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
+		switch (c) {
+		case 'i': interactive = 1; break;
+		case ARG_YES: default_yes = 1; break;
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'a': nl_cli_neigh_parse_dst(neigh, optarg); break;
+		case 'l': nl_cli_neigh_parse_lladdr(neigh, optarg); break;
+		case 'd': nl_cli_neigh_parse_dev(neigh, link_cache, optarg); break;
+		case ARG_FAMILY: nl_cli_neigh_parse_family(neigh, optarg); break;
+		case ARG_STATE: nl_cli_neigh_parse_state(neigh, optarg); break;
+		}
+ 	}
 
-	if (argc < 3 || !strcmp(argv[1], "-h")) {
-		printf("Usage: nl-neigh-delete <addr> <ifindex>\n");
-		return 2;
-	}
+	nl_cache_foreach_filter(neigh_cache, OBJ_CAST(neigh), delete_cb, NULL);
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
+	if (!quiet)
+		printf("Deleted %d neighbours\n", deleted);
 
-	neigh = rtnl_neigh_alloc();
-	if (neigh == NULL)
-		goto errout;
-
-	if (nl_connect(nlh, NETLINK_ROUTE) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout_free;
-	}
-
-	addr = nl_addr_parse(argv[1], AF_UNSPEC);
-	if (addr == NULL) {
-		fprintf(stderr, "Invalid address \"%s\"\n", argv[1]);
-		goto errout_close;
-	}
-	rtnl_neigh_set_dst(neigh, addr);
-	nl_addr_put(addr);
-
-	rtnl_neigh_set_ifindex(neigh, strtoul(argv[2], NULL, 0));
-
-	if (rtnl_neigh_delete(nlh, neigh, 0) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout_close;
-	}
-
-	err = 0;
-
-errout_close:
-	nl_close(nlh);
-errout_free:
-	rtnl_neigh_put(neigh);
-errout:
-	nl_handle_destroy(nlh);
-	return err;
+	return 0;
 }
diff --git a/src/nl-neigh-dump.c b/src/nl-neigh-dump.c
deleted file mode 100644
index 4553f2e..0000000
--- a/src/nl-neigh-dump.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * src/nl-neigh-dump.c     Dump neighbour attributes
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-	printf(
-	"Usage: nl-neigh-dump <mode> [<filter>]\n"
-	"  mode := { brief | detailed | stats | xml }\n"
-	"  filter := [dev DEV] [dst ADDR] [lladdr ADDR]\n");
-	exit(1);
-}
-
-#include "f_neigh.c"
-
-int main(int argc, char *argv[])
-{
-	struct nl_handle *nlh;
-	struct nl_cache *link_cache, *neigh_cache;
-	struct rtnl_neigh *neigh;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF
-	};
-	int err = 1;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
-	
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
-
-	neigh = rtnl_neigh_alloc();
-	if (neigh == NULL)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_close;
-
-	neigh_cache = nltool_alloc_neigh_cache(nlh);
-	if (!neigh_cache)
-		goto errout_link_cache;
-
-	params.dp_type = nltool_parse_dumptype(argv[1]);
-	if (params.dp_type < 0)
-		goto errout_neigh_cache;
-
-	get_filter(neigh, argc, argv, 2, neigh_cache);
-
-	nl_cache_dump_filter(neigh_cache, &params, (struct nl_object *) neigh);
-
-	err = 0;
-
-errout_neigh_cache:
-	nl_cache_free(neigh_cache);
-errout_link_cache:
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout_free:
-	rtnl_neigh_put(neigh);
-errout:
-	nl_handle_destroy(nlh);
-	return err;
-}
diff --git a/src/nl-neigh-list.c b/src/nl-neigh-list.c
new file mode 100644
index 0000000..ebf5486
--- /dev/null
+++ b/src/nl-neigh-list.c
@@ -0,0 +1,89 @@
+/*
+ * src/nl-neigh-list.c      List Neighbours
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/neigh.h>
+#include <netlink/cli/link.h>
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-neigh-list [OPTION]... [NEIGHBOUR]\n"
+	"\n"
+	"Options\n"
+	" -f, --format=TYPE     Output format { brief | details | stats }\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	"\n"
+	"Neighbour Options\n"
+	" -a, --addr=ADDR       Destination address of neighbour\n"
+	" -l, --lladdr=ADDR     Link layer address of neighbour\n"
+	" -d, --dev=DEV         Device the neighbour is connected to\n"
+	"     --family=FAMILY   Destination address family\n"
+	"     --state=STATE     Neighbour state, (default = permanent)\n"
+	);
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct rtnl_neigh *neigh;
+	struct nl_cache *link_cache, *neigh_cache;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+ 
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	neigh_cache = nl_cli_neigh_alloc_cache(sock);
+ 	neigh = nl_cli_neigh_alloc();
+ 
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_STATE = 258,
+		};
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "addr", 1, 0, 'a' },
+			{ "lladdr", 1, 0, 'l' },
+			{ "dev", 1, 0, 'd' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ "state", 1, 0, ARG_STATE },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "f:hva:l:d:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'a': nl_cli_neigh_parse_dst(neigh, optarg); break;
+		case 'l': nl_cli_neigh_parse_lladdr(neigh, optarg); break;
+		case 'd': nl_cli_neigh_parse_dev(neigh, link_cache, optarg); break;
+		case ARG_FAMILY: nl_cli_neigh_parse_family(neigh, optarg); break;
+		case ARG_STATE: nl_cli_neigh_parse_state(neigh, optarg); break;
+		}
+ 	}
+
+	nl_cache_dump_filter(neigh_cache, &params, OBJ_CAST(neigh));
+
+	return 0;
+}
diff --git a/src/nl-neightbl-dump.c b/src/nl-neightbl-dump.c
deleted file mode 100644
index 0d79711..0000000
--- a/src/nl-neightbl-dump.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * src/nl-neightbl-dump.c     Dump neighbour tables
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-	printf(
-	"Usage: nl-neightbl-dump <mode> [<filter>]\n"
-	"  mode := { brief | detailed | stats | xml }\n"
-	"  filter :=\n");
-	exit(1);
-}
-
-int main(int argc, char *argv[])
-{
-	int err = -1;
-	struct nl_handle *nlh;
-	struct nl_cache *ntc, *lc;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF,
-	};
-
-	if (argc < 2)
-		print_usage();
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout;
-
-	ntc = nltool_alloc_neightbl_cache(nlh);
-	if (!ntc)
-		goto errout_close;
-
-	lc = nltool_alloc_link_cache(nlh);
-	if (!lc)
-		goto errout_ntbl_cache;
-
-	params.dp_type = nltool_parse_dumptype(argv[1]);
-	if (params.dp_type < 0)
-		goto errout_link_cache;
-
-	nl_cache_dump(ntc, &params);
-	err = 0;
-
-errout_link_cache:
-	nl_cache_free(lc);
-errout_ntbl_cache:
-	nl_cache_free(ntc);
-errout_close:
-	nl_close(nlh);
-errout:
-	nl_handle_destroy(nlh);
-	return err;
-}
diff --git a/src/nl-neightbl-list.c b/src/nl-neightbl-list.c
new file mode 100644
index 0000000..5010b92
--- /dev/null
+++ b/src/nl-neightbl-list.c
@@ -0,0 +1,66 @@
+/*
+ * src/nl-neightbl-list.c     Dump neighbour tables
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-neightbl-list [OPTION]...\n"
+	"\n"
+	"Options\n"
+	" -f, --format=TYPE     Output format { brief | details | stats }\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	);
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct nl_cache *link_cache, *neightbl_cache;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+ 
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	neightbl_cache = nl_cli_alloc_cache(sock, "neighbour table",
+					    rtnl_neightbl_alloc_cache);
+ 
+	for (;;) {
+		int c, optidx = 0;
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "f:hv", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		}
+ 	}
+
+	nl_cache_dump(neightbl_cache, &params);
+
+	return 0;
+}
diff --git a/src/nl-pktloc-lookup.c b/src/nl-pktloc-lookup.c
new file mode 100644
index 0000000..09b04b2
--- /dev/null
+++ b/src/nl-pktloc-lookup.c
@@ -0,0 +1,37 @@
+/*
+ * src/nl-pktloc-lookup.c     Lookup packet location alias
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/route/pktloc.h>
+
+static void print_usage(void)
+{
+	printf("Usage: nl-pktloc-lookup <name>\n");
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct rtnl_pktloc *loc;
+	int err;
+
+	if (argc < 2)
+		print_usage();
+
+	if ((err = rtnl_pktloc_lookup(argv[1], &loc)) < 0)
+		nl_cli_fatal(err, "Unable to lookup packet location: %s",
+			nl_geterror(err));
+
+	printf("%s: %u %u+%u 0x%x %u\n", loc->name, loc->align,
+		loc->layer, loc->offset, loc->mask, loc->flags);
+
+	return 0;
+}
diff --git a/src/nl-qdisc-delete.c b/src/nl-qdisc-delete.c
index b8a17ca..5cb7aa3 100644
--- a/src/nl-qdisc-delete.c
+++ b/src/nl-qdisc-delete.c
@@ -1,76 +1,116 @@
 /*
- * src/nl-qdisc-delete.c     Delete Qdiscs
+ * src/nl-qdisc-delete.c     Delete Queuing Disciplines
  *
  *	This library is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU Lesser General Public
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/qdisc.h>
+#include <netlink/cli/link.h>
+
+static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
+struct nl_sock *sock;
 
 static void print_usage(void)
 {
-	printf("Usage: nl-qdisc-delete <ifindex> <parent> <handle>\n");
-	exit(1);
+	printf(
+	"Usage: nl-qdisc-delete [OPTION]... [QDISC]\n"
+	"\n"
+	"Options\n"
+	" -i, --interactive     Run interactively\n"
+	"     --yes             Set default answer to yes\n"
+	" -q, --quiet           Do not print informal notifications\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	"\n"
+	"QDisc Options\n"
+	" -d, --dev=DEV         Device the qdisc is attached to\n"
+	" -p, --parent=HANDLE   Identifier of parent qdisc\n"
+	" -H, --handle=HANDLE   Identifier\n"
+	" -k, --kind=NAME       Kind of qdisc (e.g. pfifo_fast)\n"
+	);
+
+	exit(0);
+}
+
+static void delete_cb(struct nl_object *obj, void *arg)
+{
+	struct rtnl_qdisc *qdisc = nl_object_priv(obj);
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+	int err;
+
+	if (interactive && !nl_cli_confirm(obj, &params, default_yes))
+		return;
+
+	if ((err = rtnl_qdisc_delete(sock, qdisc)) < 0)
+		nl_cli_fatal(err, "Unable to delete qdisc: %s\n", nl_geterror(err));
+
+	if (!quiet) {
+		printf("Deleted ");
+		nl_object_dump(obj, &params);
+	}
+
+	deleted++;
 }
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
 	struct rtnl_qdisc *qdisc;
-	uint32_t handle, parent;
-	int err = 1;
+	struct nl_cache *link_cache, *qdisc_cache;
+ 
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	qdisc_cache = nl_cli_qdisc_alloc_cache(sock);
+ 	qdisc = nl_cli_qdisc_alloc();
+ 
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_YES = 257,
+		};
+		static struct option long_opts[] = {
+			{ "interactive", 0, 0, 'i' },
+			{ "yes", 0, 0, ARG_YES },
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "dev", 1, 0, 'd' },
+			{ "parent", 1, 0, 'p' },
+			{ "handle", 1, 0, 'H' },
+			{ "kind", 1, 0, 'k' },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "iqhvd:p:H:k:", long_opts, &optidx);
+		if (c == -1)
+			break;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 3 || !strcmp(argv[1], "-h"))
-		print_usage();
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		goto errout;
-
-	qdisc = rtnl_qdisc_alloc();
-	if (!qdisc)
-		goto errout_free_handle;
-
-	rtnl_qdisc_set_ifindex(qdisc, strtoul(argv[1], NULL, 0));
-
-	if (rtnl_tc_str2handle(argv[2], &parent) < 0) {
-		fprintf(stderr, "%s\n", nl_geterror());
-		goto errout_free_qdisc;
-	}
-
-	if (argc > 3) {
-		if (rtnl_tc_str2handle(argv[3], &handle) < 0) {
-			fprintf(stderr, "%s\n", nl_geterror());
-			goto errout_free_qdisc;
+		switch (c) {
+		case 'i': interactive = 1; break;
+		case ARG_YES: default_yes = 1; break;
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'd': nl_cli_qdisc_parse_dev(qdisc, link_cache, optarg); break;
+		case 'p': nl_cli_qdisc_parse_parent(qdisc, optarg); break;
+		case 'H': nl_cli_qdisc_parse_handle(qdisc, optarg); break;
+		case 'k': nl_cli_qdisc_parse_kind(qdisc, optarg); break;
 		}
+ 	}
 
-		rtnl_qdisc_set_handle(qdisc, handle);
-	}
+	nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(qdisc), delete_cb, NULL);
 
-	rtnl_qdisc_set_parent(qdisc, parent);
+	if (!quiet)
+		printf("Deleted %d qdiscs\n", deleted);
 
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free_qdisc;
-
-	if (rtnl_qdisc_delete(nlh, qdisc) < 0) {
-		fprintf(stderr, "Unable to delete Qdisc: %s\n", nl_geterror());
-		goto errout_close;
-	}
-
-	err = 0;
-errout_close:
-	nl_close(nlh);
-errout_free_qdisc:
-	rtnl_qdisc_put(qdisc);
-errout_free_handle:
-	nl_handle_destroy(nlh);
-errout:
-	return err;
+	return 0;
 }
diff --git a/src/nl-qdisc-dump.c b/src/nl-qdisc-dump.c
deleted file mode 100644
index 167dc7f..0000000
--- a/src/nl-qdisc-dump.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * src/nl-qdisc-dump.c     Dump qdisc attributes
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-	printf(
-"Usage: nl-qdisc-dump <mode>\n"
-"  mode := { brief | detailed | stats | xml }\n");
-	exit(1);
-}
-
-int main(int argc, char *argv[])
-{
-	struct nl_handle *nlh;
-	struct nl_cache *link_cache, *qdisc_cache;
-	struct rtnl_qdisc *qdisc;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF
-	};
-	int err = 1;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
-
-	params.dp_type = nltool_parse_dumptype(argv[1]);
-	if (params.dp_type < 0)
-		return -1;
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
-
-	qdisc = rtnl_qdisc_alloc();
-	if (!qdisc)
-		goto errout_no_put;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout;
-
-	qdisc_cache = nltool_alloc_qdisc_cache(nlh);
-	if (!qdisc_cache)
-		goto errout_link_cache;
-
-	nl_cache_dump_filter(qdisc_cache, &params, (struct nl_object *) qdisc);
-	nl_cache_free(qdisc_cache);
-	err = 0;
-
-errout_link_cache:
-	nl_cache_free(link_cache);
-errout:
-	rtnl_qdisc_put(qdisc);
-errout_no_put:
-	nl_close(nlh);
-	nl_handle_destroy(nlh);
-	return err;
-}
diff --git a/src/nl-qdisc-list.c b/src/nl-qdisc-list.c
new file mode 100644
index 0000000..ec6e25f
--- /dev/null
+++ b/src/nl-qdisc-list.c
@@ -0,0 +1,90 @@
+/*
+ * src/nl-qdisc-list.c     List Qdiscs
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/qdisc.h>
+#include <netlink/cli/link.h>
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-qdisc-list [OPTION]... [QDISC]\n"
+	"\n"
+	"Options\n"
+	" -f, --format=TYPE     Output format { brief | details | stats }\n"
+	" -h, --help            Show this help\n"
+	" -v, --version         Show versioning information\n"
+	"\n"
+	"QDisc Options\n"
+	" -d, --dev=DEV         Device the qdisc is attached to\n"
+	" -p, --parent=HANDLE   Identifier of parent qdisc\n"
+	" -H, --handle=HANDLE   Identifier\n"
+	" -k, --kind=NAME       Kind of qdisc (e.g. pfifo_fast)\n"
+	);
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct rtnl_qdisc *qdisc;
+	struct nl_cache *link_cache, *qdisc_cache;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+ 
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	qdisc_cache = nl_cli_qdisc_alloc_cache(sock);
+ 	qdisc = nl_cli_qdisc_alloc();
+ 
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_YES = 257,
+		};
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "dev", 1, 0, 'd' },
+			{ "parent", 1, 0, 'p' },
+			{ "handle", 1, 0, 'H' },
+			{ "kind", 1, 0, 'k' },
+			{ 0, 0, 0, 0 }
+		};
+	
+		c = getopt_long(argc, argv, "f:qhvd:p:H:k:",
+				long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'd': nl_cli_qdisc_parse_dev(qdisc, link_cache, optarg); break;
+		case 'p': nl_cli_qdisc_parse_parent(qdisc, optarg); break;
+		case 'H': nl_cli_qdisc_parse_handle(qdisc, optarg); break;
+		case 'k': nl_cli_qdisc_parse_kind(qdisc, optarg); break;
+		}
+ 	}
+
+	nl_cache_dump_filter(qdisc_cache, &params, OBJ_CAST(qdisc));
+
+	return 0;
+}
diff --git a/src/nl-route-add.c b/src/nl-route-add.c
index 2686397..2f187df 100644
--- a/src/nl-route-add.c
+++ b/src/nl-route-add.c
@@ -1,76 +1,132 @@
 /*
- * src/nl-route-dump.c     Dump route attributes
+ * src/nl-route-add.c     Route addition utility
  *
  *	This library is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU Lesser General Public
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/route.h>
+#include <netlink/cli/link.h>
+
+static int quiet = 0;
+static struct nl_cache *link_cache, *route_cache;
 
 static void print_usage(void)
 {
 	printf(
-	"Usage: nl-route-add [<filter>]\n");
-	exit(1);
+	"Usage: nl-route-add [OPTION]... [ROUTE]\n"
+	"\n"
+	"Options\n"
+	" -q, --quiet		Do not print informal notifications\n"
+	" -h, --help            Show this help\n"
+	" -v, --version		Show versioning information\n"
+	"\n"
+	"Route Options\n"
+	" -d, --dst=ADDR        destination prefix, e.g. 10.10.0.0/16\n"
+	" -n, --nexthop=NH      nexthop configuration:\n"
+	"                         dev=DEV         route via device\n"
+	"                         weight=WEIGHT   weight of nexthop\n"
+	"                         flags=FLAGS\n"
+	"                         via=GATEWAY     route via other node\n"
+	"                         realms=REALMS\n"
+	"                         e.g. dev=eth0,via=192.168.1.12\n"
+	" -t, --table=TABLE     Routing table\n"
+	"     --family=FAMILY	Address family\n"
+	"     --src=ADDR        Source prefix\n"
+	"     --iif=DEV         Incomming interface\n"
+	"     --pref-src=ADDR   Preferred source address\n"
+	"     --metrics=OPTS    Metrics configurations\n"
+	"     --priority=NUM    Priotity\n"
+	"     --scope=SCOPE     Scope\n"
+	"     --protocol=PROTO  Protocol\n"
+	"     --type=TYPE       { unicast | local | broadcast | multicast }\n"
+	);
+	exit(0);
 }
 
-#include "f_route.c"
-
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
-	struct nl_cache *link_cache, *route_cache;
+	struct nl_sock *sock;
 	struct rtnl_route *route;
+	struct nl_dump_params dp = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
 	int err = 1;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	route_cache = nl_cli_route_alloc_cache(sock, 0);
+	route = nl_cli_route_alloc();
 
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_SRC = 258,
+			ARG_IIF,
+			ARG_PREF_SRC,
+			ARG_METRICS,
+			ARG_PRIORITY,
+			ARG_SCOPE,
+			ARG_PROTOCOL,
+			ARG_TYPE,
+		};
+		static struct option long_opts[] = {
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "dst", 1, 0, 'd' },
+			{ "nexthop", 1, 0, 'n' },
+			{ "table", 1, 0, 't' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ "src", 1, 0, ARG_SRC },
+			{ "iif", 1, 0, ARG_IIF },
+			{ "pref-src", 1, 0, ARG_PREF_SRC },
+			{ "metrics", 1, 0, ARG_METRICS },
+			{ "priority", 1, 0, ARG_PRIORITY },
+			{ "scope", 1, 0, ARG_SCOPE },
+			{ "protocol", 1, 0, ARG_PROTOCOL },
+			{ "type", 1, 0, ARG_TYPE },
+			{ 0, 0, 0, 0 }
+		};
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		goto errout;
+		c = getopt_long(argc, argv, "qhvd:n:t:", long_opts, &optidx);
+		if (c == -1)
+			break;
 
-	route = rtnl_route_alloc();
-	if (!route)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_close;
-
-	route_cache = nltool_alloc_route_cache(nlh);
-	if (!route_cache)
-		goto errout_link_cache;
-
-	get_filter(route, argc, argv, 1, route_cache, link_cache);
-
-	if (rtnl_route_add(nlh, route, 0) < 0) {
-		fprintf(stderr, "rtnl_route_add failed: %s\n",
-		nl_geterror());
-		goto errout_route_cache;
+		switch (c) {
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'd': nl_cli_route_parse_dst(route, optarg); break;
+		case 'n': nl_cli_route_parse_nexthop(route, optarg, link_cache); break;
+		case 't': nl_cli_route_parse_table(route, optarg); break;
+		case ARG_FAMILY: nl_cli_route_parse_family(route, optarg); break;
+		case ARG_SRC: nl_cli_route_parse_src(route, optarg); break;
+		case ARG_IIF: nl_cli_route_parse_iif(route, optarg, link_cache); break;
+		case ARG_PREF_SRC: nl_cli_route_parse_pref_src(route, optarg); break;
+		case ARG_METRICS: nl_cli_route_parse_metric(route, optarg); break;
+		case ARG_PRIORITY: nl_cli_route_parse_prio(route, optarg); break;
+		case ARG_SCOPE: nl_cli_route_parse_scope(route, optarg); break;
+		case ARG_PROTOCOL: nl_cli_route_parse_protocol(route, optarg); break;
+		case ARG_TYPE: nl_cli_route_parse_type(route, optarg); break;
+		}
 	}
 
-	err = 0;
+	if ((err = rtnl_route_add(sock, route, 0)) < 0)
+		nl_cli_fatal(err, "Unable to add route: %s", nl_geterror(err));
 
-errout_route_cache:
-	nl_cache_free(route_cache);
-errout_link_cache:
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout_free:
-	rtnl_route_put(route);
-errout:
-	nl_handle_destroy(nlh);
-	return err;
+	if (!quiet) {
+		printf("Added ");
+		nl_object_dump(OBJ_CAST(route), &dp);
+	}
+
+	return 0;
 }
diff --git a/src/nl-route-del.c b/src/nl-route-del.c
deleted file mode 100644
index 9d912e1..0000000
--- a/src/nl-route-del.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * src/nl-route-del.c     Delete Routes
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-	printf(
-	"Usage: nl-route-del [<filter>]\n");
-	exit(1);
-}
-
-#include "f_route.c"
-
-int main(int argc, char *argv[])
-{
-	struct nl_handle *nlh;
-	struct nl_cache *link_cache, *route_cache;
-	struct rtnl_route *route;
-	int err = 1;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		goto errout;
-
-	route = rtnl_route_alloc();
-	if (!route)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_close;
-
-	route_cache = nltool_alloc_route_cache(nlh);
-	if (!route_cache)
-		goto errout_link_cache;
-
-	get_filter(route, argc, argv, 1, route_cache, link_cache);
-
-	if (rtnl_route_del(nlh, route, 0) < 0) {
-		fprintf(stderr, "rtnl_route_del failed: %s\n",
-		nl_geterror());
-		goto errout_route_cache;
-	}
-
-	err = 0;
-
-errout_route_cache:
-	nl_cache_free(route_cache);
-errout_link_cache:
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout_free:
-	rtnl_route_put(route);
-errout:
-	nl_handle_destroy(nlh);
-	return err;
-}
diff --git a/src/nl-route-delete.c b/src/nl-route-delete.c
new file mode 100644
index 0000000..884fd7f
--- /dev/null
+++ b/src/nl-route-delete.c
@@ -0,0 +1,168 @@
+/*
+ * src/nl-route-delete.c     Delete Routes
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/route.h>
+#include <netlink/cli/link.h>
+
+static int interactive = 0, default_yes = 0, quiet = 0;
+static int deleted = 0;
+static struct nl_sock *sock;
+
+static void print_version(void)
+{
+	fprintf(stderr, "%s\n", LIBNL_STRING);
+	exit(0);
+}
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-route-delete [OPTION]... [ROUTE]\n"
+	"\n"
+	"Options\n"
+	" -i, --interactive     Run interactively\n"
+	"     --yes             Set default answer to yes\n"
+	" -q, --quiet		Do not print informal notifications\n"
+	" -h, --help            Show this help\n"
+	" -v, --version		Show versioning information\n"
+	"\n"
+	"Route Options\n"
+	" -d, --dst=ADDR        destination prefix, e.g. 10.10.0.0/16\n"
+	" -n, --nexthop=NH      nexthop configuration:\n"
+	"                         dev=DEV         route via device\n"
+	"                         weight=WEIGHT   weight of nexthop\n"
+	"                         flags=FLAGS\n"
+	"                         via=GATEWAY     route via other node\n"
+	"                         realms=REALMS\n"
+	"                         e.g. dev=eth0,via=192.168.1.12\n"
+	" -t, --table=TABLE     Routing table\n"
+	"     --family=FAMILY	Address family\n"
+	"     --src=ADDR        Source prefix\n"
+	"     --iif=DEV         Incomming interface\n"
+	"     --pref-src=ADDR   Preferred source address\n"
+	"     --metrics=OPTS    Metrics configurations\n"
+	"     --priority=NUM    Priotity\n"
+	"     --scope=SCOPE     Scope\n"
+	"     --protocol=PROTO  Protocol\n"
+	"     --type=TYPE       { unicast | local | broadcast | multicast }\n"
+	);
+	exit(0);
+}
+
+static void delete_cb(struct nl_object *obj, void *arg)
+{
+	struct rtnl_route *route = (struct rtnl_route *) obj;
+	struct nl_dump_params params = {
+		.dp_type = NL_DUMP_LINE,
+		.dp_fd = stdout,
+	};
+	int err;
+
+	if (interactive && !nl_cli_confirm(obj, &params, default_yes))
+		return;
+
+	if ((err = rtnl_route_delete(sock, route, 0)) < 0)
+		nl_cli_fatal(err, "Unable to delete route: %s", nl_geterror(err));
+
+	if (!quiet) {
+		printf("Deleted ");
+		nl_object_dump(obj, &params);
+	}
+
+	deleted++;
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_cache *link_cache, *route_cache;
+	struct rtnl_route *route;
+	int nf = 0;
+
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	route_cache = nl_cli_route_alloc_cache(sock, 0);
+	route = nl_cli_route_alloc();
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_SRC = 258,
+			ARG_IIF,
+			ARG_PREF_SRC,
+			ARG_METRICS,
+			ARG_PRIORITY,
+			ARG_SCOPE,
+			ARG_PROTOCOL,
+			ARG_TYPE,
+			ARG_YES,
+		};
+		static struct option long_opts[] = {
+			{ "interactive", 0, 0, 'i' },
+			{ "yes", 0, 0, ARG_YES },
+			{ "quiet", 0, 0, 'q' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "dst", 1, 0, 'd' },
+			{ "nexthop", 1, 0, 'n' },
+			{ "table", 1, 0, 't' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ "src", 1, 0, ARG_SRC },
+			{ "iif", 1, 0, ARG_IIF },
+			{ "pref-src", 1, 0, ARG_PREF_SRC },
+			{ "metrics", 1, 0, ARG_METRICS },
+			{ "priority", 1, 0, ARG_PRIORITY },
+			{ "scope", 1, 0, ARG_SCOPE },
+			{ "protocol", 1, 0, ARG_PROTOCOL },
+			{ "type", 1, 0, ARG_TYPE },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "iqhvd:n:t:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'i': interactive = 1; break;
+		case ARG_YES: default_yes = 1; break;
+		case 'q': quiet = 1; break;
+		case 'h': print_usage(); break;
+		case 'v': print_version(); break;
+		case 'd': nf++; nl_cli_route_parse_dst(route, optarg); break;
+		case 'n': nf++; nl_cli_route_parse_nexthop(route, optarg, link_cache); break;
+		case 't': nf++; nl_cli_route_parse_table(route, optarg); break;
+		case ARG_FAMILY: nf++; nl_cli_route_parse_family(route, optarg); break;
+		case ARG_SRC: nf++; nl_cli_route_parse_src(route, optarg); break;
+		case ARG_IIF: nf++; nl_cli_route_parse_iif(route, optarg, link_cache); break;
+		case ARG_PREF_SRC: nf++; nl_cli_route_parse_pref_src(route, optarg); break;
+		case ARG_METRICS: nf++; nl_cli_route_parse_metric(route, optarg); break;
+		case ARG_PRIORITY: nf++; nl_cli_route_parse_prio(route, optarg); break;
+		case ARG_SCOPE: nf++; nl_cli_route_parse_scope(route, optarg); break;
+		case ARG_PROTOCOL: nf++; nl_cli_route_parse_protocol(route, optarg); break;
+		case ARG_TYPE: nf++; nl_cli_route_parse_type(route, optarg); break;
+		}
+	}
+
+	if (nf == 0 && !interactive && !default_yes) {
+		fprintf(stderr, "You attempted to delete all routes in "
+			"non-interactive mode, aborting.\n");
+		exit(0);
+	}
+
+	nl_cache_foreach_filter(route_cache, OBJ_CAST(route), delete_cb, NULL);
+
+	if (!quiet)
+		printf("Deleted %d routes\n", deleted);
+
+	return 0;
+}
diff --git a/src/nl-route-dump.c b/src/nl-route-dump.c
deleted file mode 100644
index aed9bd2..0000000
--- a/src/nl-route-dump.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * src/nl-route-dump.c     Dump route attributes
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-	printf(
-	"Usage: nl-route-dump <mode> [<filter>]\n"
-	"  mode := { brief | detailed | stats | xml }\n");
-	exit(1);
-}
-
-#include "f_route.c"
-
-int main(int argc, char *argv[])
-{
-	struct nl_handle *nlh;
-	struct nl_cache *link_cache, *route_cache;
-	struct rtnl_route *route;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF
-	};
-	int err = 1;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		goto errout;
-
-	route = rtnl_route_alloc();
-	if (!route)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_close;
-
-	route_cache = nltool_alloc_route_cache(nlh);
-	if (!route_cache)
-		goto errout_link_cache;
-
-	params.dp_type = nltool_parse_dumptype(argv[1]);
-	if (params.dp_type < 0)
-		goto errout_route_cache;
-
-	get_filter(route, argc, argv, 2, route_cache, link_cache);
-
-	nl_cache_dump_filter(route_cache, &params, (struct nl_object *) route);
-
-	err = 0;
-
-errout_route_cache:
-	nl_cache_free(route_cache);
-errout_link_cache:
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout_free:
-	rtnl_route_put(route);
-errout:
-	nl_handle_destroy(nlh);
-	return err;
-}
diff --git a/src/nl-route-get.c b/src/nl-route-get.c
index fd7a503..c2f07d4 100644
--- a/src/nl-route-get.c
+++ b/src/nl-route-get.c
@@ -6,10 +6,12 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/route.h>
+#include <netlink/cli/link.h>
 
 static void print_usage(void)
 {
@@ -17,48 +19,43 @@
 	exit(1);
 }
 
+static void parse_cb(struct nl_object *obj, void *arg)
+{
+	//struct rtnl_route *route = (struct rtnl_route *) obj;
+	struct nl_dump_params params = {
+		.dp_fd = stdout,
+		.dp_type = NL_DUMP_DETAILS,
+	};
+
+	nl_object_dump(obj, &params);
+}
+
 static int cb(struct nl_msg *msg, void *arg)
 {
-	nl_cache_parse_and_add(arg, msg);
+	int err;
+
+	if ((err = nl_msg_parse(msg, &parse_cb, NULL)) < 0)
+		nl_cli_fatal(err, "Unable to parse object: %s", nl_geterror(err));
 
 	return 0;
 }
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *nlh;
+	struct nl_sock *sock;
 	struct nl_cache *link_cache, *route_cache;
 	struct nl_addr *dst;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF
-	};
 	int err = 1;
 
 	if (argc < 2 || !strcmp(argv[1], "-h"))
 		print_usage();
 
-	if (nltool_init(argc, argv) < 0)
-		goto errout;
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	route_cache = nl_cli_route_alloc_cache(sock, 0);
 
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free_handle;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_close;
-
-	dst = nltool_addr_parse(argv[1]);
-	if (!dst)
-		goto errout_link_cache;
-
-	route_cache = nltool_alloc_route_cache(nlh);
-	if (!route_cache)
-		goto errout_addr_put;
+	dst = nl_cli_addr_parse(argv[1], AF_INET);
 
 	{
 		struct nl_msg *m;
@@ -71,36 +68,18 @@
 		nlmsg_append(m, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO);
 		nla_put_addr(m, RTA_DST, dst);
 
-		if ((err = nl_send_auto_complete(nlh, m)) < 0) {
-			nlmsg_free(m);
-			fprintf(stderr, "%s\n", nl_geterror());
-			goto errout_route_cache;
-		}
-
+		err = nl_send_auto_complete(sock, m);
 		nlmsg_free(m);
+		if (err < 0)
+			nl_cli_fatal(err, "%s", nl_geterror(err));
 
-		nl_socket_modify_cb(nlh, NL_CB_VALID, NL_CB_CUSTOM, cb,
-				 route_cache);
+		nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, cb, NULL);
 
-		if (nl_recvmsgs_default(nlh) < 0) {
-			fprintf(stderr, "%s\n", nl_geterror());
-			goto errout_route_cache;
-		}
+		if (nl_recvmsgs_default(sock) < 0)
+			nl_cli_fatal(err, "%s", nl_geterror(err));
 	}
 
-	nl_cache_dump(route_cache, &params);
+	//nl_cache_dump(route_cache, &params);
 
-	err = 0;
-errout_route_cache:
-	nl_cache_free(route_cache);
-errout_addr_put:
-	nl_addr_put(dst);
-errout_link_cache:
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout_free_handle:
-	nl_handle_destroy(nlh);
-errout:
-	return err;
+	return 0;
 }
diff --git a/src/nl-route-list.c b/src/nl-route-list.c
new file mode 100644
index 0000000..e0e57be
--- /dev/null
+++ b/src/nl-route-list.c
@@ -0,0 +1,129 @@
+/*
+ * src/nl-route-list.c     List route attributes
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/route.h>
+#include <netlink/cli/link.h>
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-route-list [OPTION]... [ROUTE]\n"
+	"\n"
+	"Options\n"
+	" -c, --cache           List the contents of the route cache\n"
+	" -f, --format=TYPE	Output format { brief | details | stats }\n"
+	" -h, --help            Show this help\n"
+	" -v, --version		Show versioning information\n"
+	"\n"
+	"Route Options\n"
+	" -d, --dst=ADDR        destination prefix, e.g. 10.10.0.0/16\n"
+	" -n, --nexthop=NH      nexthop configuration:\n"
+	"                         dev=DEV         route via device\n"
+	"                         weight=WEIGHT   weight of nexthop\n"
+	"                         flags=FLAGS\n"
+	"                         via=GATEWAY     route via other node\n"
+	"                         realms=REALMS\n"
+	"                         e.g. dev=eth0,via=192.168.1.12\n"
+	" -t, --table=TABLE     Routing table\n"
+	"     --family=FAMILY	Address family\n"
+	"     --src=ADDR        Source prefix\n"
+	"     --iif=DEV         Incomming interface\n"
+	"     --pref-src=ADDR   Preferred source address\n"
+	"     --metrics=OPTS    Metrics configurations\n"
+	"     --priority=NUM    Priotity\n"
+	"     --scope=SCOPE     Scope\n"
+	"     --protocol=PROTO  Protocol\n"
+	"     --type=TYPE       { unicast | local | broadcast | multicast }\n"
+	);
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct nl_cache *link_cache, *route_cache;
+	struct rtnl_route *route;
+	struct nl_dump_params params = {
+		.dp_fd = stdout,
+		.dp_type = NL_DUMP_LINE,
+	};
+	int print_cache = 0;
+
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	route = nl_cli_route_alloc();
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_SRC = 258,
+			ARG_IIF,
+			ARG_PREF_SRC,
+			ARG_METRICS,
+			ARG_PRIORITY,
+			ARG_SCOPE,
+			ARG_PROTOCOL,
+			ARG_TYPE,
+		};
+		static struct option long_opts[] = {
+			{ "cache", 0, 0, 'c' },
+			{ "format", 1, 0, 'f' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "dst", 1, 0, 'd' },
+			{ "nexthop", 1, 0, 'n' },
+			{ "table", 1, 0, 't' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ "src", 1, 0, ARG_SRC },
+			{ "iif", 1, 0, ARG_IIF },
+			{ "pref-src", 1, 0, ARG_PREF_SRC },
+			{ "metrics", 1, 0, ARG_METRICS },
+			{ "priority", 1, 0, ARG_PRIORITY },
+			{ "scope", 1, 0, ARG_SCOPE },
+			{ "protocol", 1, 0, ARG_PROTOCOL },
+			{ "type", 1, 0, ARG_TYPE },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "cf:hvd:n:t:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'c': print_cache = 1; break;
+		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case 'd': nl_cli_route_parse_dst(route, optarg); break;
+		case 'n': nl_cli_route_parse_nexthop(route, optarg, link_cache); break;
+		case 't': nl_cli_route_parse_table(route, optarg); break;
+		case ARG_FAMILY: nl_cli_route_parse_family(route, optarg); break;
+		case ARG_SRC: nl_cli_route_parse_src(route, optarg); break;
+		case ARG_IIF: nl_cli_route_parse_iif(route, optarg, link_cache); break;
+		case ARG_PREF_SRC: nl_cli_route_parse_pref_src(route, optarg); break;
+		case ARG_METRICS: nl_cli_route_parse_metric(route, optarg); break;
+		case ARG_PRIORITY: nl_cli_route_parse_prio(route, optarg); break;
+		case ARG_SCOPE: nl_cli_route_parse_scope(route, optarg); break;
+		case ARG_PROTOCOL: nl_cli_route_parse_protocol(route, optarg); break;
+		case ARG_TYPE: nl_cli_route_parse_type(route, optarg); break;
+		}
+	}
+
+	route_cache = nl_cli_route_alloc_cache(sock,
+				print_cache ? ROUTE_CACHE_CONTENT : 0);
+
+	nl_cache_dump_filter(route_cache, &params, OBJ_CAST(route));
+
+	return 0;
+}
diff --git a/src/nl-rule-dump.c b/src/nl-rule-dump.c
deleted file mode 100644
index f9e483a..0000000
--- a/src/nl-rule-dump.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * src/nl-rule-dump.c     Dump rule attributes
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-static void print_usage(void)
-{
-	printf(
-	"Usage: nl-rule-dump <mode> [<filter>]\n"
-	"  mode := { brief | detailed | stats | xml }\n");
-	exit(1);
-}
-
-int main(int argc, char *argv[])
-{
-	struct nl_handle *nlh;
-	struct nl_cache *link_cache, *rule_cache;
-	struct rtnl_rule *rule;
-	struct nl_dump_params params = {
-		.dp_fd = stdout,
-		.dp_type = NL_DUMP_BRIEF
-	};
-	int err = 1;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	if (argc < 2 || !strcmp(argv[1], "-h"))
-		print_usage();
-
-	nlh = nltool_alloc_handle();
-	if (!nlh)
-		return -1;
-
-	rule = rtnl_rule_alloc();
-	if (!rule)
-		goto errout;
-
-	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
-		goto errout_free;
-
-	link_cache = nltool_alloc_link_cache(nlh);
-	if (!link_cache)
-		goto errout_close;
-
-	rule_cache = nltool_alloc_rule_cache(nlh);
-	if (!rule_cache)
-		goto errout_link_cache;
-
-	params.dp_type = nltool_parse_dumptype(argv[1]);
-	if (params.dp_type < 0)
-		goto errout_rule_cache;
-
-	//get_filter(route, argc, argv, 2, route_cache);
-
-	nl_cache_dump_filter(rule_cache, &params, (struct nl_object *) rule);
-
-	err = 0;
-
-errout_rule_cache:
-	nl_cache_free(rule_cache);
-errout_link_cache:
-	nl_cache_free(link_cache);
-errout_close:
-	nl_close(nlh);
-errout_free:
-	nl_object_put((struct nl_object *) rule);
-errout:
-	return err;
-}
diff --git a/src/nl-rule-list.c b/src/nl-rule-list.c
new file mode 100644
index 0000000..8b474fa
--- /dev/null
+++ b/src/nl-rule-list.c
@@ -0,0 +1,77 @@
+/*
+ * src/nl-rule-dump.c     Dump rule attributes
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/rule.h>
+#include <netlink/cli/link.h>
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-rule-list [OPTION]... [ROUTE]\n"
+	"\n"
+	"Options\n"
+	" -c, --cache           List the contents of the route cache\n"
+	" -f, --format=TYPE	Output format { brief | details | stats }\n"
+	" -h, --help            Show this help\n"
+	" -v, --version		Show versioning information\n"
+	"\n"
+	"Rule Options\n"
+	"     --family          Address family\n"
+	);
+	exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *sock;
+	struct rtnl_rule *rule;
+	struct nl_cache *link_cache, *rule_cache;
+	struct nl_dump_params params = {
+		.dp_fd = stdout,
+		.dp_type = NL_DUMP_LINE,
+	};
+
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	rule_cache = nl_cli_rule_alloc_cache(sock);
+	rule = nl_cli_rule_alloc();
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+		};
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ "family", 1, 0, ARG_FAMILY },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "f:hv", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		case ARG_FAMILY: nl_cli_rule_parse_family(rule, optarg); break;
+		}
+	}
+
+	nl_cache_dump_filter(rule_cache, &params, OBJ_CAST(rule));
+
+	return 0;
+}
diff --git a/src/nl-tctree-dump.c b/src/nl-tctree-dump.c
deleted file mode 100644
index 8b45e7b..0000000
--- a/src/nl-tctree-dump.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * src/nl-tctree-dump.c		Dump Traffic Control Tree
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-#include <linux/pkt_sched.h>
-
-static struct nl_handle *nl_handle;
-static struct nl_cache *qdisc_cache, *class_cache;
-static struct nl_dump_params dump_params = {
-	.dp_type = NL_DUMP_FULL,
-};
-
-static int ifindex;
-static void print_qdisc(struct nl_object *, void *);
-
-static void print_class(struct nl_object *obj, void *arg)
-{
-	struct rtnl_qdisc *leaf;
-	struct rtnl_class *class = (struct rtnl_class *) obj;
-	struct nl_cache *cls_cache;
-	uint32_t parent = rtnl_class_get_handle(class);
-
-	dump_params.dp_prefix = (int)(long) arg;
-	nl_object_dump(obj, &dump_params);
-
-	leaf = rtnl_class_leaf_qdisc(class, qdisc_cache);
-	if (leaf)
-		print_qdisc((struct nl_object *) leaf, arg + 2);
-
-	rtnl_class_foreach_child(class, class_cache, &print_class, arg + 2);
-
-	cls_cache = rtnl_cls_alloc_cache(nl_handle, ifindex, parent);
-	if (!cls_cache)
-		return;
-
-	dump_params.dp_prefix = (int)(long) arg + 2;
-	nl_cache_dump(cls_cache, &dump_params);
-	nl_cache_free(cls_cache);
-}
-
-static void print_qdisc(struct nl_object *obj, void *arg)
-{
-	struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj;
-	struct nl_cache *cls_cache;
-	uint32_t parent = rtnl_qdisc_get_handle(qdisc);
-
-	dump_params.dp_prefix = (int)(long) arg;
-	nl_object_dump(obj, &dump_params);
-
-	rtnl_qdisc_foreach_child(qdisc, class_cache, &print_class, arg + 2);
-
-	cls_cache = rtnl_cls_alloc_cache(nl_handle, ifindex, parent);
-	if (!cls_cache)
-		return;
-
-	dump_params.dp_prefix = (int)(long) arg + 2;
-	nl_cache_dump(cls_cache, &dump_params);
-	nl_cache_free(cls_cache);
-}
-
-static void print_link(struct nl_object *obj, void *arg)
-{
-	struct rtnl_link *link = (struct rtnl_link *) obj;
-	struct rtnl_qdisc *qdisc;
-
-	ifindex = rtnl_link_get_ifindex(link);
-	dump_params.dp_prefix = 0;
-	nl_object_dump(obj, &dump_params);
-
-	class_cache = rtnl_class_alloc_cache(nl_handle, ifindex);
-	if (!class_cache)
-		return;
-
-	qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT);
-	if (qdisc) {
-		print_qdisc((struct nl_object *) qdisc, (void *) 2);
-		rtnl_qdisc_put(qdisc);
-	}
-
-	qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, 0);
-	if (qdisc) {
-		print_qdisc((struct nl_object *) qdisc, (void *) 2);
-		rtnl_qdisc_put(qdisc);
-	}
-
-	qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_INGRESS);
-	if (qdisc) {
-		print_qdisc((struct nl_object *) qdisc, (void *) 2);
-		rtnl_qdisc_put(qdisc);
-	}
-
-	nl_cache_free(class_cache);
-}
-
-int main(int argc, char *argv[])
-{
-	struct nl_cache *link_cache;
-
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	dump_params.dp_fd = stdout;
-
-	if (argc > 1) {
-		if (!strcasecmp(argv[1], "brief"))
-			dump_params.dp_type = NL_DUMP_BRIEF;
-		else if (!strcasecmp(argv[1], "full"))
-			dump_params.dp_type = NL_DUMP_FULL;
-		else if (!strcasecmp(argv[1], "stats"))
-			dump_params.dp_type = NL_DUMP_STATS;
-	}
-
-	nl_handle = nltool_alloc_handle();
-	if (!nl_handle)
-		return 1;
-
-	if (nltool_connect(nl_handle, NETLINK_ROUTE) < 0)
-		return 1;
-
-	link_cache = nltool_alloc_link_cache(nl_handle);
-	if (!link_cache)
-		return 1;
-
-	qdisc_cache = nltool_alloc_qdisc_cache(nl_handle);
-	if (!qdisc_cache)
-		return 1;
-
-	nl_cache_foreach(link_cache, &print_link, NULL);
-
-	nl_cache_free(qdisc_cache);
-	nl_cache_free(link_cache);
-
-	nl_close(nl_handle);
-	nl_handle_destroy(nl_handle);
-	return 0;
-}
diff --git a/src/nl-tctree-list.c b/src/nl-tctree-list.c
new file mode 100644
index 0000000..a074a51
--- /dev/null
+++ b/src/nl-tctree-list.c
@@ -0,0 +1,149 @@
+/*
+ * src/nl-tctree-list.c		List Traffic Control Tree
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
+#include <netlink/cli/qdisc.h>
+#include <linux/pkt_sched.h>
+
+static struct nl_sock *sock;
+static struct nl_cache *qdisc_cache, *class_cache;
+static struct nl_dump_params params = {
+	.dp_type = NL_DUMP_DETAILS,
+};
+
+static int ifindex;
+static void print_qdisc(struct nl_object *, void *);
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nl-tctree-list [OPTION]...\n"
+	"\n"
+	"Options\n"
+	" -f, --format=TYPE	Output format { brief | details | stats }\n"
+	" -h, --help            Show this help\n"
+	" -v, --version		Show versioning information\n"
+	);
+	exit(0);
+}
+
+static void print_class(struct nl_object *obj, void *arg)
+{
+	struct rtnl_qdisc *leaf;
+	struct rtnl_class *class = (struct rtnl_class *) obj;
+	struct nl_cache *cls_cache;
+	uint32_t parent = rtnl_class_get_handle(class);
+
+	params.dp_prefix = (int)(long) arg;
+	nl_object_dump(obj, &params);
+
+	leaf = rtnl_class_leaf_qdisc(class, qdisc_cache);
+	if (leaf)
+		print_qdisc((struct nl_object *) leaf, arg + 2);
+
+	rtnl_class_foreach_child(class, class_cache, &print_class, arg + 2);
+
+	if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0)
+		return;
+
+	params.dp_prefix = (int)(long) arg + 2;
+	nl_cache_dump(cls_cache, &params);
+	nl_cache_free(cls_cache);
+}
+
+static void print_qdisc(struct nl_object *obj, void *arg)
+{
+	struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj;
+	struct nl_cache *cls_cache;
+	uint32_t parent = rtnl_qdisc_get_handle(qdisc);
+
+	params.dp_prefix = (int)(long) arg;
+	nl_object_dump(obj, &params);
+
+	rtnl_qdisc_foreach_child(qdisc, class_cache, &print_class, arg + 2);
+
+	if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0)
+		return;
+
+	params.dp_prefix = (int)(long) arg + 2;
+	nl_cache_dump(cls_cache, &params);
+	nl_cache_free(cls_cache);
+}
+
+static void print_link(struct nl_object *obj, void *arg)
+{
+	struct rtnl_link *link = (struct rtnl_link *) obj;
+	struct rtnl_qdisc *qdisc;
+
+	ifindex = rtnl_link_get_ifindex(link);
+	params.dp_prefix = 0;
+	nl_object_dump(obj, &params);
+
+	if (rtnl_class_alloc_cache(sock, ifindex, &class_cache) < 0)
+		return;
+
+	qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT);
+	if (qdisc) {
+		print_qdisc((struct nl_object *) qdisc, (void *) 2);
+		rtnl_qdisc_put(qdisc);
+	}
+
+	qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, 0);
+	if (qdisc) {
+		print_qdisc((struct nl_object *) qdisc, (void *) 2);
+		rtnl_qdisc_put(qdisc);
+	}
+
+	qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_INGRESS);
+	if (qdisc) {
+		print_qdisc((struct nl_object *) qdisc, (void *) 2);
+		rtnl_qdisc_put(qdisc);
+	}
+
+	nl_cache_free(class_cache);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_cache *link_cache;
+
+	sock = nl_cli_alloc_socket();
+	nl_cli_connect(sock, NETLINK_ROUTE);
+	link_cache = nl_cli_link_alloc_cache(sock);
+	qdisc_cache = nl_cli_qdisc_alloc_cache(sock);
+
+	params.dp_fd = stdout;
+
+	for (;;) {
+		int c, optidx = 0;
+		static struct option long_opts[] = {
+			{ "format", 1, 0, 'f' },
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'v' },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "f:hv", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+		case 'h': print_usage(); break;
+		case 'v': nl_cli_print_version(); break;
+		}
+	}
+
+	nl_cache_foreach(link_cache, &print_link, NULL);
+
+	return 0;
+}
diff --git a/src/nl-util-addr.c b/src/nl-util-addr.c
index 9f12795..5f0738d 100644
--- a/src/nl-util-addr.c
+++ b/src/nl-util-addr.c
@@ -6,10 +6,10 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
  */
 
-#include "utils.h"
+#include <netlink/cli/utils.h>
 
 int main(int argc, char *argv[])
 {
@@ -21,19 +21,12 @@
 		fprintf(stderr, "Usage: nl-util-addr <address>\n");
 		return -1;
 	}
-	
-	a = nl_addr_parse(argv[1], AF_UNSPEC);
-	if (a == NULL) {
-		fprintf(stderr, "Cannot parse address \"%s\"\n", argv[1]);
-		return -1;
-	}
 
+	a = nl_cli_addr_parse(argv[1], AF_UNSPEC);
 	err = nl_addr_resolve(a, host, sizeof(host));
-	if (err != 0) {
-		fprintf(stderr, "Cannot resolve address \"%s\": %d\n",
-			argv[1], err);
-		return -1;
-	}
+	if (err != 0)
+		nl_cli_fatal(err, "Unable to resolve address \"%s\": %s",
+		      argv[1], nl_geterror(err));
 
 	printf("%s\n", host);
 
diff --git a/src/utils.c b/src/utils.c
deleted file mode 100644
index b43758a..0000000
--- a/src/utils.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * src/utils.c		Utilities
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-#include <stdlib.h>
-
-int nltool_init(int argc, char *argv[])
-{
-	return 0;
-}
-
-int nltool_connect(struct nl_handle *nlh, int protocol)
-{
-	int err;
-
-	err = nl_connect(nlh, protocol);
-	if (err < 0)
-		fprintf(stderr, "Unable to connect netlink socket%s\n",
-			nl_geterror());
-
-	return err;
-}
-
-struct nl_handle *nltool_alloc_handle(void)
-{
-	return nl_handle_alloc();
-}
-
-struct nl_addr *nltool_addr_parse(const char *str)
-{
-	struct nl_addr *addr;
-
-	addr = nl_addr_parse(str, AF_UNSPEC);
-	if (!addr)
-		fprintf(stderr, "Unable to parse address \"%s\": %s\n",
-			str, nl_geterror());
-
-	return addr;
-}
-
-int nltool_parse_dumptype(const char *str)
-{
-	if (!strcasecmp(str, "brief"))
-		return NL_DUMP_BRIEF;
-	else if (!strcasecmp(str, "detailed"))
-		return NL_DUMP_FULL;
-	else if (!strcasecmp(str, "stats"))
-		return NL_DUMP_STATS;
-	else if (!strcasecmp(str, "xml"))
-		return NL_DUMP_XML;
-	else if (!strcasecmp(str, "env"))
-		return NL_DUMP_ENV;
-	else {
-		fprintf(stderr, "Invalid dump type \"%s\".\n", str);
-		return -1;
-	}
-}
-
-struct nl_cache *nltool_alloc_link_cache(struct nl_handle *nlh)
-{
-	struct nl_cache *cache;
-
-	cache = rtnl_link_alloc_cache(nlh);
-	if (!cache)
-		fprintf(stderr, "Unable to retrieve link cache: %s\n",
-			nl_geterror());
-	else
-		nl_cache_mngt_provide(cache);
-
-	return cache;
-}
-
-struct nl_cache *nltool_alloc_addr_cache(struct nl_handle *nlh)
-{
-	struct nl_cache *cache;
-
-	cache = rtnl_addr_alloc_cache(nlh);
-	if (!cache)
-		fprintf(stderr, "Unable to retrieve address cache: %s\n",
-			nl_geterror());
-	else
-		nl_cache_mngt_provide(cache);
-
-	return cache;
-}
-
-struct nl_cache *nltool_alloc_neigh_cache(struct nl_handle *nlh)
-{
-	struct nl_cache *cache;
-
-	cache = rtnl_neigh_alloc_cache(nlh);
-	if (!cache)
-		fprintf(stderr, "Unable to retrieve neighbour cache: %s\n",
-			nl_geterror());
-	else
-		nl_cache_mngt_provide(cache);
-
-	return cache;
-}
-
-struct nl_cache *nltool_alloc_neightbl_cache(struct nl_handle *nlh)
-{
-	struct nl_cache *cache;
-
-	cache = rtnl_neightbl_alloc_cache(nlh);
-	if (!cache)
-		fprintf(stderr, "Unable to retrieve neighbour table "
-				"cache: %s\n", nl_geterror());
-	else
-		nl_cache_mngt_provide(cache);
-
-	return cache;
-}
-
-struct nl_cache *nltool_alloc_route_cache(struct nl_handle *nlh)
-{
-	struct nl_cache *cache;
-
-	cache = rtnl_route_alloc_cache(nlh);
-	if (!cache)
-		fprintf(stderr, "Unable to retrieve route cache: %s\n",
-			nl_geterror());
-	else
-		nl_cache_mngt_provide(cache);
-
-	return cache;
-}
-
-struct nl_cache *nltool_alloc_rule_cache(struct nl_handle *nlh)
-{
-	struct nl_cache *cache;
-
-	cache = rtnl_rule_alloc_cache(nlh);
-	if (!cache)
-		fprintf(stderr, "Unable to retrieve rule cache: %s\n",
-			nl_geterror());
-	else
-		nl_cache_mngt_provide(cache);
-
-	return cache;
-}
-
-struct nl_cache *nltool_alloc_qdisc_cache(struct nl_handle *nlh)
-{
-	struct nl_cache *cache;
-
-	cache = rtnl_qdisc_alloc_cache(nlh);
-	if (!cache)
-		fprintf(stderr, "Unable to retrieve qdisc cache: %s\n",
-			nl_geterror());
-	else
-		nl_cache_mngt_provide(cache);
-
-	return cache;
-}
-
-struct nl_cache *nltool_alloc_genl_family_cache(struct nl_handle *nlh)
-{
-	struct nl_cache *cache;
-
-	cache = genl_ctrl_alloc_cache(nlh);
-	if (!cache)
-		fprintf(stderr, "Unable to retrieve genl family cache: %s\n",
-			nl_geterror());
-	else
-		nl_cache_mngt_provide(cache);
-
-	return cache;
-}
diff --git a/src/utils.h b/src/utils.h
deleted file mode 100644
index d738ce7..0000000
--- a/src/utils.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * src/utils.h		Utilities
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
-
-#ifndef __SRC_UTILS_H_
-#define __SRC_UTILS_H_
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <errno.h>
-#include <stdint.h>
-#include <getopt.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <netlink-local.h>
-#include <netlink/netlink.h>
-#include <netlink/utils.h>
-#include <netlink/addr.h>
-#include <netlink/route/rtnl.h>
-#include <netlink/route/link.h>
-#include <netlink/route/addr.h>
-#include <netlink/route/neighbour.h>
-#include <netlink/route/neightbl.h>
-#include <netlink/route/route.h>
-#include <netlink/route/rule.h>
-#include <netlink/route/qdisc.h>
-#include <netlink/route/class.h>
-#include <netlink/route/classifier.h>
-#include <netlink/fib_lookup/lookup.h>
-#include <netlink/fib_lookup/request.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
-#include <netlink/genl/mngt.h>
-#include <netlink/netfilter/ct.h>
-
-extern int nltool_init(int argc, char *argv[]);
-extern int nltool_connect(struct nl_handle *nlh, int protocol);
-extern struct nl_addr *nltool_addr_parse(const char *str);
-extern int nltool_parse_dumptype(const char *str);
-extern struct nl_handle *nltool_alloc_handle(void);
-
-extern struct nl_cache *nltool_alloc_link_cache(struct nl_handle *nlh);
-extern struct nl_cache *nltool_alloc_addr_cache(struct nl_handle *nlh);
-extern struct nl_cache *nltool_alloc_neigh_cache(struct nl_handle *nlh);
-extern struct nl_cache *nltool_alloc_neightbl_cache(struct nl_handle *nlh);
-extern struct nl_cache *nltool_alloc_route_cache(struct nl_handle *nlh);
-extern struct nl_cache *nltool_alloc_rule_cache(struct nl_handle *nlh);
-extern struct nl_cache *nltool_alloc_qdisc_cache(struct nl_handle *nlh);
-extern struct nl_cache *nltool_alloc_genl_family_cache(struct nl_handle *nlh);
-
-#define arg_match(str) !strcasecmp(argv[idx], str)
-
-#endif
diff --git a/tests/Makefile b/tests/Makefile
index b5cec34..8494eea 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -23,7 +23,7 @@
 
 test-%: test-%.c
 	@echo "  LD $@"; \
-	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -lnl-genl -lnl-route
 
 clean:
 	@echo "  CLEAN src"; \
diff --git a/tests/test-cache-mngr.c b/tests/test-cache-mngr.c
index 4d70e31..777bce8 100644
--- a/tests/test-cache-mngr.c
+++ b/tests/test-cache-mngr.c
@@ -1,10 +1,13 @@
 #include "../src/utils.h"
+#include <signal.h>
+
+static int quit = 0;
 
 static void change_cb(struct nl_cache *cache, struct nl_object *obj,
 		      int action)
 {
 	struct nl_dump_params dp = {
-		.dp_type = NL_DUMP_BRIEF,
+		.dp_type = NL_DUMP_LINE,
 		.dp_fd = stdout,
 	};
 
@@ -18,56 +21,51 @@
 	nl_object_dump(obj, &dp);
 }
 
+static void sigint(int arg)
+{
+	quit = 1;
+}
+
 int main(int argc, char *argv[])
 {
 	struct nl_cache_mngr *mngr;
 	struct nl_cache *lc, *nc, *ac, *rc;
-	struct nl_handle *handle;
+	struct nl_sock *sock;
+	int err;
 
-	nltool_init(argc, argv);
+	signal(SIGINT, sigint);
 
-	handle = nltool_alloc_handle();
+	sock = nlt_alloc_socket();
+	err = nl_cache_mngr_alloc(sock, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr);
+	if (err < 0)
+		fatal(err, "Unable to allocate cache manager: %s",
+		      nl_geterror(err));
 
-	mngr = nl_cache_mngr_alloc(handle, NETLINK_ROUTE, NL_AUTO_PROVIDE);
-	if (!mngr) {
-		nl_perror("nl_cache_mngr_alloc");
-		return -1;
-	}
+	if ((err = nl_cache_mngr_add(mngr, "route/link", &change_cb, &lc)) < 0)
+		fatal(err, "Unable to add cache route/link: %s",
+		      nl_geterror(err));
 
-	lc = nl_cache_mngr_add(mngr, "route/link", &change_cb);
-	if (lc == NULL) {
-		nl_perror("nl_cache_mngr_add(route/link");
-		return -1;
-	}
+	if ((err = nl_cache_mngr_add(mngr, "route/neigh", &change_cb, &nc)) < 0)
+		fatal(err, "Unable to add cache route/neigh: %s",
+		      nl_geterror(err));
 
-	nc = nl_cache_mngr_add(mngr, "route/neigh", &change_cb);
-	if (nc == NULL) {
-		nl_perror("nl_cache_mngr_add(route/neigh");
-		return -1;
-	}
+	if ((err = nl_cache_mngr_add(mngr, "route/addr", &change_cb, &ac)) < 0)
+		fatal(err, "Unable to add cache route/addr: %s",
+		      nl_geterror(err));
 
-	ac = nl_cache_mngr_add(mngr, "route/addr", &change_cb);
-	if (ac == NULL) {
-		nl_perror("nl_cache_mngr_add(route/addr");
-		return -1;
-	}
+	if ((err = nl_cache_mngr_add(mngr, "route/route", &change_cb, &rc)) < 0)
+		fatal(err, "Unable to add cache route/route: %s",
+		      nl_geterror(err));
 
-	rc = nl_cache_mngr_add(mngr, "route/route", &change_cb);
-	if (rc == NULL) {
-		nl_perror("nl_cache_mngr_add(route/route");
-		return -1;
-	}
-
-	for (;;) {
+	while (!quit) {
 		int err = nl_cache_mngr_poll(mngr, 5000);
-		if (err < 0) {
-			nl_perror("nl_cache_mngr_poll()");
-			return -1;
-		}
+		if (err < 0 && err != -NLE_INTR)
+			fatal(err, "Polling failed: %s", nl_geterror(err));
 
 	}
 
 	nl_cache_mngr_free(mngr);
+	nl_socket_free(sock);
 
 	return 0;
 }
diff --git a/tests/test-genl.c b/tests/test-genl.c
index e44b3fb..8bf60c5 100644
--- a/tests/test-genl.c
+++ b/tests/test-genl.c
@@ -2,55 +2,35 @@
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *h;
+	struct nl_sock *sock;
 	struct nl_msg *msg;
 	void *hdr;
+	int err;
 
-	if (nltool_init(argc, argv) < 0)
-		return -1;
-
-	h = nltool_alloc_handle();
-	if (!h) {
-		nl_perror("nl_handle_alloc");
-		return -1;
-	}
-
-	if (genl_connect(h) < 0) {
-		nl_perror("genl_connect");
-		return -1;
-	}
+	sock = nlt_alloc_socket();
+	nlt_connect(sock, NETLINK_GENERIC);
 
 	msg = nlmsg_alloc();
-	if (msg == NULL) {
-		nl_perror("nlmsg_alloc");
-		return -1;
-	}
+	if (msg == NULL)
+		fatal(NLE_NOMEM, "Unable to allocate netlink message");
 
 	hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, GENL_ID_CTRL,
 			  0, 0, CTRL_CMD_GETFAMILY, 1);
-	if (hdr == NULL) {
-		nl_perror("genlmsg_put");
-		return -1;
-	}
+	if (hdr == NULL)
+		fatal(ENOMEM, "Unable to write genl header");
 
-	if (nla_put_u32(msg, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL) < 0) {
-		nl_perror("nla_put_u32(CTRL_ATTR_FAMILY_ID)");
-		return -1;
-	}
+	if ((err = nla_put_u32(msg, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL)) < 0)
+		fatal(err, "Unable to add attribute: %s", nl_geterror(err));
 
-	if (nl_send_auto_complete(h, msg) < 0) {
-		nl_perror("nl_send_auto_complete");
-		return -1;
-	}
+	if ((err = nl_send_auto_complete(sock, msg)) < 0)
+		fatal(err, "Unable to send message: %s", nl_geterror(err));
 
-	if (nl_recvmsgs_default(h) < 0) {
-		nl_perror("nl_recvmsgs_def");
-		return -1;
-	}
+	if ((err = nl_recvmsgs_default(sock)) < 0)
+		fatal(err, "Unable to receive message: %s", nl_geterror(err));
 
 	nlmsg_free(msg);
-
-	nl_close(h);
+	nl_close(sock);
+	nl_socket_free(sock);
 
 	return 0;
 }
diff --git a/tests/test-nf-cache-mngr.c b/tests/test-nf-cache-mngr.c
index 86cbabb..05485bf 100644
--- a/tests/test-nf-cache-mngr.c
+++ b/tests/test-nf-cache-mngr.c
@@ -12,7 +12,7 @@
 	if (!nl_addr_cmp(hack, nfnl_ct_get_src(ct, 1)) ||
 	    !nl_addr_cmp(hack, nfnl_ct_get_dst(ct, 1))) {
 		struct nl_dump_params dp = {
-			.dp_type = NL_DUMP_BRIEF,
+			.dp_type = NL_DUMP_LINE,
 			.dp_fd = stdout,
 		};
 
@@ -24,14 +24,12 @@
 int main(int argc, char *argv[])
 {
 	struct nl_cache_mngr *mngr;
-	struct nl_handle *handle;
+	struct nl_sock *sock;
 	struct nl_cache *ct;
 
-	nltool_init(argc, argv);
+	sock = nlt_socket_alloc();
 
-	handle = nltool_alloc_handle();
-
-	mngr = nl_cache_mngr_alloc(handle, NETLINK_NETFILTER, NL_AUTO_PROVIDE);
+	mngr = nl_cache_mngr_alloc(sock, NETLINK_NETFILTER, NL_AUTO_PROVIDE);
 	if (!mngr) {
 		nl_perror("nl_cache_mngr_alloc");
 		return -1;
diff --git a/tests/test-socket-creation.c b/tests/test-socket-creation.c
index 4066eef..a170ccd 100644
--- a/tests/test-socket-creation.c
+++ b/tests/test-socket-creation.c
@@ -2,7 +2,7 @@
 
 int main(int argc, char *argv[])
 {
-	struct nl_handle *h[1025];
+	struct nl_sock *h[1025];
 	int i;
 
 	h[0] = nl_handle_alloc();