about summary refs log tree commit diff
path: root/src/platform
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform')
-rw-r--r--src/platform/Makefile.am1
-rw-r--r--src/platform/Makefile.in753
-rw-r--r--src/platform/nm-fake-platform.c1352
-rw-r--r--src/platform/nm-fake-platform.h49
-rw-r--r--src/platform/nm-linux-platform.c4180
-rw-r--r--src/platform/nm-linux-platform.h49
-rw-r--r--src/platform/nm-platform.c2618
-rw-r--r--src/platform/nm-platform.h634
-rw-r--r--src/platform/tests/Makefile.am116
-rw-r--r--src/platform/tests/Makefile.in1702
-rw-r--r--src/platform/tests/dump.c136
-rw-r--r--src/platform/tests/monitor.c30
-rw-r--r--src/platform/tests/platform.c897
-rw-r--r--src/platform/tests/test-address.c265
-rw-r--r--src/platform/tests/test-cleanup.c95
-rw-r--r--src/platform/tests/test-common.c174
-rw-r--r--src/platform/tests/test-common.h42
-rw-r--r--src/platform/tests/test-link.c557
-rw-r--r--src/platform/tests/test-route.c255
-rw-r--r--src/platform/wifi/wifi-utils-nl80211.c971
-rw-r--r--src/platform/wifi/wifi-utils-nl80211.h30
-rw-r--r--src/platform/wifi/wifi-utils-private.h77
-rw-r--r--src/platform/wifi/wifi-utils-wext.c671
-rw-r--r--src/platform/wifi/wifi-utils-wext.h30
-rw-r--r--src/platform/wifi/wifi-utils.c225
-rw-r--r--src/platform/wifi/wifi-utils.h76
26 files changed, 15985 insertions, 0 deletions
diff --git a/src/platform/Makefile.am b/src/platform/Makefile.am
new file mode 100644
index 00000000..3c6155b9
--- /dev/null
+++ b/src/platform/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = tests
diff --git a/src/platform/Makefile.in b/src/platform/Makefile.in
new file mode 100644
index 00000000..9358879f
--- /dev/null
+++ b/src/platform/Makefile.in
@@ -0,0 +1,753 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/platform
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
+DLLTOOL = @DLLTOOL@
+DNSMASQ_PATH = @DNSMASQ_PATH@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MAKEFILE = @GLIB_MAKEFILE@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+IPTABLES_PATH = @IPTABLES_PATH@
+IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
+IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
+KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
+MOC = @MOC@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
+NM_MICRO_VERSION = @NM_MICRO_VERSION@
+NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
+NM_VERSION = @NM_VERSION@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
+QT_CFLAGS = @QT_CFLAGS@
+QT_LIBS = @QT_LIBS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@
+SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
+SUBDIRS = tests
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/platform/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/platform/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-am clean clean-generic clean-libtool cscopelist-am ctags \
+	ctags-am distclean distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+	ps ps-am tags tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
new file mode 100644
index 00000000..46272217
--- /dev/null
+++ b/src/platform/nm-fake-platform.c
@@ -0,0 +1,1352 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-platform-fake.c - Fake platform interaction code for testing NetworkManager
+ *
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012–2013 Red Hat, Inc.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+
+#include "NetworkManagerUtils.h"
+#include "nm-fake-platform.h"
+#include "nm-logging.h"
+
+#define debug(format, ...) nm_log_dbg (LOGD_PLATFORM, format, __VA_ARGS__)
+
+typedef struct {
+	GHashTable *options;
+	GArray *links;
+	GArray *ip4_addresses;
+	GArray *ip6_addresses;
+	GArray *ip4_routes;
+	GArray *ip6_routes;
+} NMFakePlatformPrivate;
+
+typedef struct {
+	NMPlatformLink link;
+
+	char *udi;
+	GBytes *address;
+	int vlan_id;
+} NMFakePlatformLink;
+
+#define NM_FAKE_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_FAKE_PLATFORM, NMFakePlatformPrivate))
+
+G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM)
+
+/******************************************************************/
+
+void
+nm_fake_platform_setup (void)
+{
+	nm_platform_setup (NM_TYPE_FAKE_PLATFORM);
+}
+
+/******************************************************************/
+
+static gboolean
+sysctl_set (NMPlatform *platform, const char *path, const char *value)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+
+	g_hash_table_insert (priv->options, g_strdup (path), g_strdup (value));
+
+	return TRUE;
+}
+
+static char *
+sysctl_get (NMPlatform *platform, const char *path)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+
+	return g_strdup (g_hash_table_lookup (priv->options, path));
+}
+
+static const char *
+type_to_type_name (NMLinkType type)
+{
+	switch (type) {
+	case NM_LINK_TYPE_UNKNOWN:
+		return "unknown";
+	case NM_LINK_TYPE_LOOPBACK:
+		return "loopback";
+	case NM_LINK_TYPE_ETHERNET:
+		return "ethernet";
+	case NM_LINK_TYPE_DUMMY:
+		return "dummy";
+	case NM_LINK_TYPE_BRIDGE:
+		return "bridge";
+	case NM_LINK_TYPE_BOND:
+		return "bond";
+	case NM_LINK_TYPE_TEAM:
+		return "team";
+	case NM_LINK_TYPE_VLAN:
+		return "vlan";
+	case NM_LINK_TYPE_NONE:
+	default:
+		return NULL;
+	}
+}
+
+static void
+link_init (NMFakePlatformLink *device, int ifindex, int type, const char *name)
+{
+	g_assert (!name || strlen (name) < sizeof(device->link.name));
+
+	memset (device, 0, sizeof (*device));
+
+	device->link.ifindex = name ? ifindex : 0;
+	device->link.type = type;
+	device->link.type_name = type_to_type_name (type);
+	device->link.driver = type_to_type_name (type);
+	device->link.udi = device->udi = g_strdup_printf ("fake:%d", ifindex);
+	if (name)
+		strcpy (device->link.name, name);
+	switch (device->link.type) {
+	case NM_LINK_TYPE_DUMMY:
+		device->link.arp = FALSE;
+		break;
+	default:
+		device->link.arp = TRUE;
+	}
+	device->address = NULL;
+}
+
+static NMFakePlatformLink *
+link_get (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	NMFakePlatformLink *device;
+
+	if (ifindex >= priv->links->len)
+		goto not_found;
+	device = &g_array_index (priv->links, NMFakePlatformLink, ifindex);
+	if (!device->link.ifindex)
+		goto not_found;
+
+	return device;
+not_found:
+	debug ("link not found: %d", ifindex);
+	platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
+	return NULL;
+}
+
+static GArray *
+link_get_all (NMPlatform *platform)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	GArray *links = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), priv->links->len);
+	int i;
+
+	for (i = 0; i < priv->links->len; i++)
+		if (g_array_index (priv->links, NMFakePlatformLink, i).link.ifindex)
+			g_array_append_val (links, g_array_index (priv->links, NMFakePlatformLink, i).link);
+
+	return links;
+}
+
+static gboolean
+_nm_platform_link_get (NMPlatform *platform, int ifindex, NMPlatformLink *link)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (device)
+		*link = device->link;
+	return !!device;
+}
+
+static gboolean
+link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	NMFakePlatformLink device;
+
+	link_init (&device, priv->links->len, type, name);
+
+	g_array_append_val (priv->links, device);
+
+	if (device.link.ifindex)
+		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, device.link.ifindex, &device, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
+
+	return TRUE;
+}
+
+static gboolean
+link_delete (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+	NMPlatformLink deleted_device;
+	int i;
+
+	if (!device || !device->link.ifindex)
+		return FALSE;
+
+	memcpy (&deleted_device, &device->link, sizeof (deleted_device));
+	memset (&device->link, 0, sizeof (device->link));
+
+	/* Remove addresses and routes which belong to the deleted interface */
+	for (i = 0; i < priv->ip4_addresses->len; i++) {
+		NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i);
+
+		if (address->ifindex == ifindex)
+			memset (address, 0, sizeof (*address));
+	}
+	for (i = 0; i < priv->ip6_addresses->len; i++) {
+		NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i);
+
+		if (address->ifindex == ifindex)
+			memset (address, 0, sizeof (*address));
+	}
+	for (i = 0; i < priv->ip4_routes->len; i++) {
+		NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i);
+
+		if (route->ifindex == ifindex)
+			memset (route, 0, sizeof (*route));
+	}
+	for (i = 0; i < priv->ip6_routes->len; i++) {
+		NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i);
+
+		if (route->ifindex == ifindex)
+			memset (route, 0, sizeof (*route));
+	}
+
+	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, ifindex, &deleted_device, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
+
+	return TRUE;
+}
+
+static int
+link_get_ifindex (NMPlatform *platform, const char *name)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	int i;
+
+	for (i = 0; i < priv->links->len; i++) {
+		NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i);
+
+		if (device && !g_strcmp0 (device->link.name, name))
+			return device->link.ifindex;
+	}
+
+	return 0;
+}
+
+static const char *
+link_get_name (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	return device ? device->link.name : NULL;
+}
+
+static NMLinkType
+link_get_type (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	return device ? device->link.type : NM_LINK_TYPE_NONE;
+}
+
+static const char *
+link_get_type_name (NMPlatform *platform, int ifindex)
+{
+	return type_to_type_name (link_get_type (platform, ifindex));
+}
+
+static void
+link_changed (NMPlatform *platform, NMFakePlatformLink *device)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	int i;
+
+	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, device->link.ifindex, &device->link, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
+
+	if (device->link.master) {
+		NMFakePlatformLink *master = link_get (platform, device->link.master);
+
+		g_return_if_fail (master != device);
+
+		master->link.connected = FALSE;
+		for (i = 0; i < priv->links->len; i++) {
+			NMFakePlatformLink *slave = &g_array_index (priv->links, NMFakePlatformLink, i);
+
+			if (slave && slave->link.master == master->link.ifindex && slave->link.connected)
+				master->link.connected = TRUE;
+		}
+
+		link_changed (platform, master);
+	}
+}
+
+static gboolean
+link_set_up (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (!device)
+		return FALSE;
+
+	device->link.up = TRUE;
+	switch (device->link.type) {
+	case NM_LINK_TYPE_DUMMY:
+	case NM_LINK_TYPE_VLAN:
+		device->link.connected = TRUE;
+		break;
+	case NM_LINK_TYPE_BRIDGE:
+	case NM_LINK_TYPE_BOND:
+	case NM_LINK_TYPE_TEAM:
+		device->link.connected = FALSE;
+		break;
+	default:
+		device->link.connected = FALSE;
+		g_error ("Unexpected device type: %d", device->link.type);
+	}
+
+	link_changed (platform, device);
+
+	return TRUE;
+}
+
+static gboolean
+link_set_down (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (!device)
+		return FALSE;
+
+	device->link.up = FALSE;
+	device->link.connected = FALSE;
+
+	link_changed (platform, device);
+
+	return TRUE;
+}
+
+static gboolean
+link_set_arp (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (!device)
+		return FALSE;
+
+	device->link.arp = TRUE;
+
+	link_changed (platform, device);
+
+	return TRUE;
+}
+
+static gboolean
+link_set_noarp (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (!device)
+		return FALSE;
+
+	device->link.arp = FALSE;
+
+	link_changed (platform, device);
+
+	return TRUE;
+}
+
+static gboolean
+link_is_up (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	return device ? device->link.up : FALSE;
+}
+
+static gboolean
+link_is_connected (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	return device ? device->link.connected : FALSE;
+}
+
+static gboolean
+link_uses_arp (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	return device ? device->link.arp : FALSE;
+}
+
+static gboolean
+link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t len)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (device->address)
+		g_bytes_unref (device->address);
+
+	device->address = g_bytes_new (addr, len);
+
+	link_changed (platform, link_get (platform, ifindex));
+
+	return TRUE;
+}
+
+static gconstpointer
+link_get_address (NMPlatform *platform, int ifindex, size_t *length)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (!device || !device->address) {
+		if (length)
+			*length = 0;
+		return NULL;
+	}
+
+	return g_bytes_get_data (device->address, length);
+}
+
+static gboolean
+link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (device) {
+		device->link.mtu = mtu;
+		link_changed (platform, device);
+	}
+
+	return !!device;
+}
+
+static guint32
+link_get_mtu (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	return device ? device->link.mtu : 0;
+}
+
+static char *
+link_get_physical_port_id (NMPlatform *platform, int ifindex)
+{
+	/* We call link_get just to cause an error to be set if @ifindex is bad. */
+	link_get (platform, ifindex);
+
+	return NULL;
+}
+
+static gboolean
+link_get_wake_on_lan (NMPlatform *platform, int ifindex)
+{
+	/* We call link_get just to cause an error to be set if @ifindex is bad. */
+	link_get (platform, ifindex);
+
+	return FALSE;
+}
+
+static gboolean
+link_supports_carrier_detect (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (!device)
+		return FALSE;
+
+	switch (device->link.type) {
+	case NM_LINK_TYPE_DUMMY:
+		return FALSE;
+	default:
+		return TRUE;
+	}
+}
+
+static gboolean
+link_supports_vlans (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (!device)
+		return FALSE;
+
+	switch (device->link.type) {
+	case NM_LINK_TYPE_LOOPBACK:
+		return FALSE;
+	default:
+		return TRUE;
+	}
+}
+
+static gboolean
+link_enslave (NMPlatform *platform, int master, int slave)
+{
+	NMFakePlatformLink *device = link_get (platform, slave);
+
+	g_return_val_if_fail (device, FALSE);
+
+	device->link.master = master;
+
+	link_changed (platform, device);
+
+	return TRUE;
+}
+
+static gboolean
+link_release (NMPlatform *platform, int master_idx, int slave_idx)
+{
+	NMFakePlatformLink *master = link_get (platform, master_idx);
+	NMFakePlatformLink *slave = link_get (platform, slave_idx);
+
+	g_return_val_if_fail (master, FALSE);
+	g_return_val_if_fail (slave, FALSE);
+
+	if (slave->link.master != master->link.ifindex) {
+		platform->error = NM_PLATFORM_ERROR_NOT_SLAVE;
+		return FALSE;
+	}
+
+	slave->link.master = 0;
+
+	link_changed (platform, slave);
+	link_changed (platform, master);
+
+	return TRUE;
+}
+
+static int
+link_get_master (NMPlatform *platform, int slave)
+{
+	NMFakePlatformLink *device = link_get (platform, slave);
+
+	g_return_val_if_fail (device, FALSE);
+
+	return device->link.master;
+}
+
+static gboolean
+master_set_option (NMPlatform *platform, int master, const char *option, const char *value)
+{
+	auto_g_free char *path = g_strdup_printf ("master:%d:%s", master, option);
+
+	return sysctl_set (platform, path, value);
+}
+
+static char *
+master_get_option (NMPlatform *platform, int master, const char *option)
+{
+	auto_g_free char *path = g_strdup_printf ("master:%d:%s", master, option);
+
+	return sysctl_get (platform, path);
+}
+
+static gboolean
+slave_set_option (NMPlatform *platform, int slave, const char *option, const char *value)
+{
+	auto_g_free char *path = g_strdup_printf ("slave:%d:%s", slave, option);
+
+	return sysctl_set (platform, path, value);
+}
+
+static char *
+slave_get_option (NMPlatform *platform, int slave, const char *option)
+{
+	auto_g_free char *path = g_strdup_printf ("slave:%d:%s", slave, option);
+
+	return sysctl_get (platform, path);
+}
+
+static gboolean
+vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags)
+{
+	NMFakePlatformLink *device;
+
+	if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0))
+		return FALSE;
+
+	device = link_get (platform, link_get_ifindex (platform, name));
+
+	g_return_val_if_fail (device, FALSE);
+
+	device->vlan_id = vlan_id;
+	device->link.parent = parent;
+
+	return TRUE;
+}
+
+static gboolean
+vlan_get_info (NMPlatform *platform, int ifindex, int *parent, int *vlan_id)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	g_return_val_if_fail (device, FALSE);
+
+	if (parent)
+		*parent = device->link.parent;
+	if (vlan_id)
+		*vlan_id = device->vlan_id;
+
+	return TRUE;
+}
+
+static gboolean
+vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to)
+{
+	return !!link_get (platform, ifindex);
+}
+
+static gboolean
+vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to)
+{
+	return !!link_get (platform, ifindex);
+}
+
+static gboolean
+infiniband_partition_add (NMPlatform *platform, int parent, int p_key)
+{
+	NMFakePlatformLink *parent_device;
+	char *name;
+	gboolean success;
+
+	parent_device = link_get (platform, parent);
+	g_return_val_if_fail (parent_device != NULL, FALSE);
+
+	name = g_strdup_printf ("%s.%04x", parent_device->link.name, p_key);
+	success = link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, 0);
+	g_free (name);
+
+	return success;
+}
+
+static gboolean
+veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props)
+{
+	return FALSE;
+}
+
+static gboolean
+tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props)
+{
+	return FALSE;
+}
+
+static gboolean
+macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProperties *props)
+{
+	return FALSE;
+}
+
+static gboolean
+vxlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformVxlanProperties *props)
+{
+	return FALSE;
+}
+
+static gboolean
+gre_get_properties (NMPlatform *platform, int ifindex, NMPlatformGreProperties *props)
+{
+	return FALSE;
+}
+
+static gboolean
+wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	g_return_val_if_fail (device, FALSE);
+
+	if (device->link.type != NM_LINK_TYPE_WIFI)
+		return FALSE;
+
+	if (caps) {
+		*caps = (  NM_WIFI_DEVICE_CAP_CIPHER_WEP40
+		         | NM_WIFI_DEVICE_CAP_CIPHER_WEP104
+		         | NM_WIFI_DEVICE_CAP_CIPHER_TKIP
+		         | NM_WIFI_DEVICE_CAP_CIPHER_CCMP
+		         | NM_WIFI_DEVICE_CAP_WPA
+		         | NM_WIFI_DEVICE_CAP_RSN
+		         | NM_WIFI_DEVICE_CAP_AP
+		         | NM_WIFI_DEVICE_CAP_ADHOC);
+	}
+	return TRUE;
+}
+
+static gboolean
+wifi_get_bssid (NMPlatform *platform, int ifindex, struct ether_addr *bssid)
+{
+	return FALSE;
+}
+
+static GByteArray *
+wifi_get_ssid (NMPlatform *platform, int ifindex)
+{
+	return NULL;
+}
+
+static guint32
+wifi_get_frequency (NMPlatform *platform, int ifindex)
+{
+	return 0;
+}
+
+static int
+wifi_get_quality (NMPlatform *platform, int ifindex)
+{
+	return 0;
+}
+
+static guint32
+wifi_get_rate (NMPlatform *platform, int ifindex)
+{
+	return 0;
+}
+
+static NM80211Mode
+wifi_get_mode (NMPlatform *platform, int ifindex)
+{
+	return NM_802_11_MODE_UNKNOWN;
+}
+
+static void
+wifi_set_mode (NMPlatform *platform, int ifindex, NM80211Mode mode)
+{
+	;
+}
+
+static guint32
+wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs)
+{
+	return freqs[0];
+}
+
+static void
+wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean running)
+{
+	;
+}
+
+static guint32
+mesh_get_channel (NMPlatform *platform, int ifindex)
+{
+	return 0;
+}
+
+static gboolean
+mesh_set_channel (NMPlatform *platform, int ifindex, guint32 channel)
+{
+	return FALSE;
+}
+
+static gboolean
+mesh_set_ssid (NMPlatform *platform, int ifindex, const GByteArray *ssid)
+{
+	return FALSE;
+}
+
+/******************************************************************/
+
+static GArray *
+ip4_address_get_all (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	GArray *addresses;
+	NMPlatformIP4Address *address;
+	int count = 0, i;
+
+	/* Count addresses */
+	for (i = 0; i < priv->ip4_addresses->len; i++) {
+		address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i);
+		if (address && address->ifindex == ifindex)
+			count++;
+	}
+
+	addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Address), count);
+
+	/* Fill addresses */
+	for (i = 0; i < priv->ip4_addresses->len; i++) {
+		address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i);
+		if (address && address->ifindex == ifindex)
+			g_array_append_val (addresses, *address);
+	}
+
+	return addresses;
+}
+
+static GArray *
+ip6_address_get_all (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	GArray *addresses;
+	NMPlatformIP6Address *address;
+	int count = 0, i;
+
+	/* Count addresses */
+	for (i = 0; i < priv->ip6_addresses->len; i++) {
+		address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i);
+		if (address && address->ifindex == ifindex)
+			count++;
+	}
+
+	addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Address), count);
+
+	/* Fill addresses */
+	count = 0;
+	for (i = 0; i < priv->ip6_addresses->len; i++) {
+		address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i);
+		if (address && address->ifindex == ifindex)
+			g_array_append_val (addresses, *address);
+	}
+
+	return addresses;
+}
+
+static gboolean
+ip4_address_add (NMPlatform *platform, int ifindex,
+                 in_addr_t addr, in_addr_t peer_addr,
+                 int plen, guint32 lifetime, guint32 preferred,
+                 const char *label)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	NMPlatformIP4Address address;
+	int i;
+
+	memset (&address, 0, sizeof (address));
+	address.source = NM_PLATFORM_SOURCE_KERNEL;
+	address.ifindex = ifindex;
+	address.address = addr;
+	address.peer_address = peer_addr;
+	address.plen = plen;
+	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
+	address.lifetime = lifetime;
+	address.preferred = preferred;
+	if (label)
+		g_strlcpy (address.label, label, sizeof (address.label));
+
+	for (i = 0; i < priv->ip4_addresses->len; i++) {
+		NMPlatformIP4Address *item = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i);
+
+		if (item->ifindex != address.ifindex)
+			continue;
+		if (item->address != address.address)
+			continue;
+		if (item->plen != address.plen)
+			continue;
+
+		memcpy (item, &address, sizeof (address));
+		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
+		return TRUE;
+	}
+
+	g_array_append_val (priv->ip4_addresses, address);
+	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
+
+	return TRUE;
+}
+
+static gboolean
+ip6_address_add (NMPlatform *platform, int ifindex,
+                 struct in6_addr addr, struct in6_addr peer_addr,
+                 int plen, guint32 lifetime, guint32 preferred, guint flags)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	NMPlatformIP6Address address;
+	int i;
+
+	memset (&address, 0, sizeof (address));
+	address.source = NM_PLATFORM_SOURCE_KERNEL;
+	address.ifindex = ifindex;
+	address.address = addr;
+	address.peer_address = peer_addr;
+	address.plen = plen;
+	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
+	address.lifetime = lifetime;
+	address.preferred = preferred;
+	address.flags = flags;
+
+	for (i = 0; i < priv->ip6_addresses->len; i++) {
+		NMPlatformIP6Address *item = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i);
+
+		if (item->ifindex != address.ifindex)
+			continue;
+		if (!IN6_ARE_ADDR_EQUAL (&item->address, &address.address))
+			continue;
+		if (item->plen != address.plen)
+			continue;
+
+		memcpy (item, &address, sizeof (address));
+		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
+		return TRUE;
+	}
+
+	g_array_append_val (priv->ip6_addresses, address);
+	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
+
+	return TRUE;
+}
+
+static gboolean
+ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	int i;
+
+	for (i = 0; i < priv->ip4_addresses->len; i++) {
+		NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i);
+
+		if (address->ifindex == ifindex && address->plen == plen && address->address == addr) {
+			NMPlatformIP4Address deleted_address;
+
+			memcpy (&deleted_address, address, sizeof (deleted_address));
+			memset (address, 0, sizeof (*address));
+			g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
+			return TRUE;
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	int i;
+
+	for (i = 0; i < priv->ip6_addresses->len; i++) {
+		NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i);
+
+		if (address->ifindex == ifindex && address->plen == plen
+				&& IN6_ARE_ADDR_EQUAL (&address->address, &addr)) {
+			NMPlatformIP6Address deleted_address;
+
+			memcpy (&deleted_address, address, sizeof (deleted_address));
+			memset (address, 0, sizeof (*address));
+			g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
+			return TRUE;
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	int i;
+
+	for (i = 0; i < priv->ip4_addresses->len; i++) {
+		NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i);
+
+		if (address->ifindex == ifindex && address->plen == plen && address->address == addr)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	int i;
+
+	for (i = 0; i < priv->ip6_addresses->len; i++) {
+		NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i);
+
+		if (address->ifindex == ifindex && address->plen == plen &&
+				IN6_ARE_ADDR_EQUAL (&address->address, &addr))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+/******************************************************************/
+
+static GArray *
+ip4_route_get_all (NMPlatform *platform, int ifindex, gboolean include_default)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	GArray *routes;
+	NMPlatformIP4Route *route;
+	int count = 0, i;
+
+	/* Count routes */
+	for (i = 0; i < priv->ip4_routes->len; i++) {
+		route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i);
+		if (route && route->ifindex == ifindex)
+			count++;
+	}
+
+	routes = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Route), count);
+
+	/* Fill routes */
+	for (i = 0; i < priv->ip4_routes->len; i++) {
+		route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i);
+		if (route && route->ifindex == ifindex) {
+			if (route->plen != 0 || include_default)
+				g_array_append_val (routes, *route);
+		}
+	}
+
+	return routes;
+}
+
+static GArray *
+ip6_route_get_all (NMPlatform *platform, int ifindex, gboolean include_default)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	GArray *routes;
+	NMPlatformIP6Route *route;
+	int count = 0, i;
+
+	/* Count routes */
+	for (i = 0; i < priv->ip6_routes->len; i++) {
+		route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i);
+		if (route && route->ifindex == ifindex)
+			count++;
+	}
+
+	routes = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Route), count);
+
+	/* Fill routes */
+	for (i = 0; i < priv->ip6_routes->len; i++) {
+		route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i);
+		if (route && route->ifindex == ifindex) {
+			if (route->plen != 0 || include_default)
+				g_array_append_val (routes, *route);
+		}
+	}
+
+	return routes;
+}
+
+static gboolean
+ip4_route_add (NMPlatform *platform, int ifindex, NMPlatformSource source,
+               in_addr_t network, int plen, in_addr_t gateway,
+               int metric, int mss)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	NMPlatformIP4Route route;
+	guint i;
+
+	memset (&route, 0, sizeof (route));
+	route.source = NM_PLATFORM_SOURCE_KERNEL;
+	route.ifindex = ifindex;
+	route.source = source;
+	route.network = network;
+	route.plen = plen;
+	route.gateway = gateway;
+	route.metric = metric;
+	route.mss = mss;
+
+	for (i = 0; i < priv->ip4_routes->len; i++) {
+		NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i);
+
+		if (item->ifindex != route.ifindex)
+			continue;
+		if (item->network != route.network)
+			continue;
+		if (item->plen != route.plen)
+			continue;
+
+		memcpy (item, &route, sizeof (route));
+		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
+		return TRUE;
+	}
+
+	g_array_append_val (priv->ip4_routes, route);
+	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
+
+	return TRUE;
+}
+
+static gboolean
+ip6_route_add (NMPlatform *platform, int ifindex, NMPlatformSource source,
+               struct in6_addr network, int plen, struct in6_addr gateway,
+               int metric, int mss)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	NMPlatformIP6Route route;
+	guint i;
+
+	memset (&route, 0, sizeof (route));
+	route.source = NM_PLATFORM_SOURCE_KERNEL;
+	route.ifindex = ifindex;
+	route.source = source;
+	route.network = network;
+	route.plen = plen;
+	route.gateway = gateway;
+	route.metric = metric;
+	route.mss = mss;
+
+	for (i = 0; i < priv->ip6_routes->len; i++) {
+		NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i);
+
+		if (item->ifindex != route.ifindex)
+			continue;
+		if (!IN6_ARE_ADDR_EQUAL (&item->network, &route.network))
+			continue;
+		if (item->plen != route.plen)
+			continue;
+
+		memcpy (item, &route, sizeof (route));
+		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL);
+		return TRUE;
+	}
+
+	g_array_append_val (priv->ip6_routes, route);
+	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL);
+
+	return TRUE;
+}
+
+static NMPlatformIP4Route *
+ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, int metric)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	int i;
+
+	for (i = 0; i < priv->ip4_routes->len; i++) {
+		NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i);
+
+		if (route->ifindex == ifindex
+				&& route->network == network
+				&& route->plen == plen
+				&& route->metric == metric)
+			return route;
+	}
+
+	return NULL;
+}
+
+static NMPlatformIP6Route *
+ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, int metric)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
+	int i;
+
+	for (i = 0; i < priv->ip6_routes->len; i++) {
+		NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i);
+
+		if (route->ifindex == ifindex
+				&& IN6_ARE_ADDR_EQUAL (&route->network, &network)
+				&& route->plen == plen
+				&& route->metric == metric)
+			return route;
+	}
+
+	return NULL;
+}
+
+static gboolean
+ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, int metric)
+{
+	NMPlatformIP4Route *route = ip4_route_get (platform, ifindex, network, plen, metric);
+	NMPlatformIP4Route deleted_route;
+
+	if (route) {
+		memcpy (&deleted_route, route, sizeof (deleted_route));
+		memset (route, 0, sizeof (*route));
+		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, int metric)
+{
+	NMPlatformIP6Route *route = ip6_route_get (platform, ifindex, network, plen, metric);
+	NMPlatformIP6Route deleted_route;
+
+	if (route) {
+		memcpy (&deleted_route, route, sizeof (deleted_route));
+		memset (route, 0, sizeof (*route));
+		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, int metric)
+{
+	return !!ip4_route_get (platform, ifindex, network, plen, metric);
+}
+
+static gboolean
+ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, int metric)
+{
+	return !!ip6_route_get (platform, ifindex, network, plen, metric);
+}
+
+/******************************************************************/
+
+static void
+nm_fake_platform_init (NMFakePlatform *fake_platform)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (fake_platform);
+
+	priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+	priv->links = g_array_new (TRUE, TRUE, sizeof (NMFakePlatformLink));
+	priv->ip4_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Address));
+	priv->ip6_addresses = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Address));
+	priv->ip4_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Route));
+	priv->ip6_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route));
+}
+
+static gboolean
+setup (NMPlatform *platform)
+{
+	/* skip zero element */
+	link_add (platform, NULL, NM_LINK_TYPE_NONE, NULL, 0);
+
+	/* add loopback interface */
+	link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, 0);
+
+	/* add some ethernets */
+	link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, 0);
+	link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, 0);
+	link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, 0);
+
+	return TRUE;
+}
+
+static void
+nm_fake_platform_finalize (GObject *object)
+{
+	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (object);
+	int i;
+
+	g_hash_table_unref (priv->options);
+	for (i = 0; i < priv->links->len; i++) {
+		NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i);
+
+		g_bytes_unref (device->address);
+		g_free (device->udi);
+	}
+	g_array_unref (priv->links);
+	g_array_unref (priv->ip4_addresses);
+	g_array_unref (priv->ip6_addresses);
+	g_array_unref (priv->ip4_routes);
+	g_array_unref (priv->ip6_routes);
+
+	G_OBJECT_CLASS (nm_fake_platform_parent_class)->finalize (object);
+}
+
+static void
+nm_fake_platform_class_init (NMFakePlatformClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMPlatformClass *platform_class = NM_PLATFORM_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMFakePlatformPrivate));
+
+	/* virtual methods */
+	object_class->finalize = nm_fake_platform_finalize;
+
+	platform_class->setup = setup;
+
+	platform_class->sysctl_set = sysctl_set;
+	platform_class->sysctl_get = sysctl_get;
+
+	platform_class->link_get = _nm_platform_link_get;
+	platform_class->link_get_all = link_get_all;
+	platform_class->link_add = link_add;
+	platform_class->link_delete = link_delete;
+	platform_class->link_get_ifindex = link_get_ifindex;
+	platform_class->link_get_name = link_get_name;
+	platform_class->link_get_type = link_get_type;
+	platform_class->link_get_type_name = link_get_type_name;
+
+	platform_class->link_set_up = link_set_up;
+	platform_class->link_set_down = link_set_down;
+	platform_class->link_set_arp = link_set_arp;
+	platform_class->link_set_noarp = link_set_noarp;
+	platform_class->link_is_up = link_is_up;
+	platform_class->link_is_connected = link_is_connected;
+	platform_class->link_uses_arp = link_uses_arp;
+
+	platform_class->link_set_address = link_set_address;
+	platform_class->link_get_address = link_get_address;
+	platform_class->link_get_mtu = link_get_mtu;
+	platform_class->link_set_mtu = link_set_mtu;
+
+	platform_class->link_get_physical_port_id = link_get_physical_port_id;
+	platform_class->link_get_wake_on_lan = link_get_wake_on_lan;
+
+	platform_class->link_supports_carrier_detect = link_supports_carrier_detect;
+	platform_class->link_supports_vlans = link_supports_vlans;
+
+	platform_class->link_enslave = link_enslave;
+	platform_class->link_release = link_release;
+	platform_class->link_get_master = link_get_master;
+	platform_class->master_set_option = master_set_option;
+	platform_class->master_get_option = master_get_option;
+	platform_class->slave_set_option = slave_set_option;
+	platform_class->slave_get_option = slave_get_option;
+
+	platform_class->vlan_add = vlan_add;
+	platform_class->vlan_get_info = vlan_get_info;
+	platform_class->vlan_set_ingress_map = vlan_set_ingress_map;
+	platform_class->vlan_set_egress_map = vlan_set_egress_map;
+
+	platform_class->infiniband_partition_add = infiniband_partition_add;
+
+	platform_class->veth_get_properties = veth_get_properties;
+	platform_class->tun_get_properties = tun_get_properties;
+	platform_class->macvlan_get_properties = macvlan_get_properties;
+	platform_class->vxlan_get_properties = vxlan_get_properties;
+	platform_class->gre_get_properties = gre_get_properties;
+
+	platform_class->wifi_get_capabilities = wifi_get_capabilities;
+	platform_class->wifi_get_bssid = wifi_get_bssid;
+	platform_class->wifi_get_ssid = wifi_get_ssid;
+	platform_class->wifi_get_frequency = wifi_get_frequency;
+	platform_class->wifi_get_quality = wifi_get_quality;
+	platform_class->wifi_get_rate = wifi_get_rate;
+	platform_class->wifi_get_mode = wifi_get_mode;
+	platform_class->wifi_set_mode = wifi_set_mode;
+	platform_class->wifi_find_frequency = wifi_find_frequency;
+	platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running;
+
+	platform_class->mesh_get_channel = mesh_get_channel;
+	platform_class->mesh_set_channel = mesh_set_channel;
+	platform_class->mesh_set_ssid = mesh_set_ssid;
+
+	platform_class->ip4_address_get_all = ip4_address_get_all;
+	platform_class->ip6_address_get_all = ip6_address_get_all;
+	platform_class->ip4_address_add = ip4_address_add;
+	platform_class->ip6_address_add = ip6_address_add;
+	platform_class->ip4_address_delete = ip4_address_delete;
+	platform_class->ip6_address_delete = ip6_address_delete;
+	platform_class->ip4_address_exists = ip4_address_exists;
+	platform_class->ip6_address_exists = ip6_address_exists;
+
+	platform_class->ip4_route_get_all = ip4_route_get_all;
+	platform_class->ip6_route_get_all = ip6_route_get_all;
+	platform_class->ip4_route_add = ip4_route_add;
+	platform_class->ip6_route_add = ip6_route_add;
+	platform_class->ip4_route_delete = ip4_route_delete;
+	platform_class->ip6_route_delete = ip6_route_delete;
+	platform_class->ip4_route_exists = ip4_route_exists;
+	platform_class->ip6_route_exists = ip6_route_exists;
+}
diff --git a/src/platform/nm-fake-platform.h b/src/platform/nm-fake-platform.h
new file mode 100644
index 00000000..c9fa42eb
--- /dev/null
+++ b/src/platform/nm-fake-platform.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-fake-platform.h - Fake platform interaction code for testing NetworkManager
+ *
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_FAKE_PLATFORM_H
+#define NM_FAKE_PLATFORM_H
+
+#include "nm-platform.h"
+
+#define NM_TYPE_FAKE_PLATFORM            (nm_fake_platform_get_type ())
+#define NM_FAKE_PLATFORM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatform))
+#define NM_FAKE_PLATFORM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_FAKE_PLATFORM, NMFakePlatformClass))
+#define NM_IS_FAKE_PLATFORM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_FAKE_PLATFORM))
+#define NM_IS_FAKE_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_FAKE_PLATFORM))
+#define NM_FAKE_PLATFORM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatformClass))
+
+/******************************************************************/
+
+typedef struct {
+	NMPlatform parent;
+} NMFakePlatform;
+
+typedef struct {
+	NMPlatformClass parent;
+} NMFakePlatformClass;
+
+/******************************************************************/
+
+GType nm_fake_platform_get_type (void);
+
+void nm_fake_platform_setup (void);
+
+#endif /* NM_FAKE_PLATFORM_H */
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
new file mode 100644
index 00000000..f73ff320
--- /dev/null
+++ b/src/platform/nm-linux-platform.c
@@ -0,0 +1,4180 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-linux-platform.c - Linux kernel & udev network configuration layer
+ *
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012-2013 Red Hat, Inc.
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/if_link.h>
+#include <linux/if_tun.h>
+#include <linux/if_tunnel.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <netlink/netlink.h>
+#include <netlink/object.h>
+#include <netlink/cache.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+#include <netlink/route/addr.h>
+#include <netlink/route/route.h>
+#include <gudev/gudev.h>
+
+#include "NetworkManagerUtils.h"
+#include "nm-linux-platform.h"
+#include "NetworkManagerUtils.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "wifi/wifi-utils.h"
+#include "wifi/wifi-utils-wext.h"
+
+/* This is only included for the translation of VLAN flags */
+#include "nm-setting-vlan.h"
+
+#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__)
+#define warning(...) nm_log_warn (LOGD_PLATFORM, __VA_ARGS__)
+#define error(...) nm_log_err (LOGD_PLATFORM, __VA_ARGS__)
+
+
+struct libnl_vtable
+{
+	void *handle;
+
+	int (*f_nl_has_capability) (int capability);
+};
+
+
+typedef struct {
+	struct nl_sock *nlh;
+	struct nl_sock *nlh_event;
+	struct nl_cache *link_cache;
+	struct nl_cache *address_cache;
+	struct nl_cache *route_cache;
+	GIOChannel *event_channel;
+	guint event_id;
+
+	GUdevClient *udev_client;
+	GHashTable *udev_devices;
+
+	GHashTable *wifi_data;
+
+	int support_kernel_extended_ifa_flags;
+} NMLinuxPlatformPrivate;
+
+#define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate))
+
+G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM)
+
+static const char *to_string_object (NMPlatform *platform, struct nl_object *obj);
+
+void
+nm_linux_platform_setup (void)
+{
+	nm_platform_setup (NM_TYPE_LINUX_PLATFORM);
+}
+
+/******************************************************************/
+
+static int
+_nl_f_nl_has_capability (int capability)
+{
+	return FALSE;
+}
+
+static struct libnl_vtable *
+_nl_get_vtable ()
+{
+	static struct libnl_vtable vtable;
+
+	if (G_UNLIKELY (!vtable.f_nl_has_capability)) {
+		void *handle;
+
+		handle = dlopen ("libnl-3.so", RTLD_LAZY | RTLD_NOLOAD);
+		if (handle) {
+			vtable.handle = handle;
+			vtable.f_nl_has_capability = dlsym (handle, "nl_has_capability");
+		}
+
+		if (!vtable.f_nl_has_capability)
+			vtable.f_nl_has_capability = &_nl_f_nl_has_capability;
+
+		g_return_val_if_fail (vtable.handle, &vtable);
+	}
+
+	return &vtable;
+}
+
+static gboolean
+_nl_has_capability (int capability)
+{
+	return (_nl_get_vtable ()->f_nl_has_capability) (capability);
+}
+
+/******************************************************************/
+
+static guint32
+_get_expiry (guint32 now_s, guint32 lifetime_s)
+{
+	gint64 t = ((gint64) now_s) + ((gint64) lifetime_s);
+
+	return MIN (t, NM_PLATFORM_LIFETIME_PERMANENT - 1);
+}
+
+/* The rtnl_addr object contains relative lifetimes @valid and @preferred
+ * that count in seconds, starting from the moment when the kernel constructed
+ * the netlink message.
+ *
+ * There is also a field rtnl_addr_last_update_time(), which is the absolute
+ * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address
+ * was modified (wrapping every 497 days).
+ * Immediately at the time when the address was last modified, #NOW and @last_update_time
+ * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time.
+ * However, this is not true in general. As time goes by, whenever kernel sends a new address
+ * via netlink, the lifetimes keep counting down.
+ *
+ * As we cache the rtnl_addr object we must know the absolute expiries.
+ * As a hack, modify the relative timestamps valid and preferred into absolute
+ * timestamps of scale nm_utils_get_monotonic_timestamp_s().
+ **/
+static void
+_rtnl_addr_hack_lifetimes_rel_to_abs (struct rtnl_addr *rtnladdr)
+{
+	guint32 a_valid  = rtnl_addr_get_valid_lifetime (rtnladdr);
+	guint32 a_preferred = rtnl_addr_get_preferred_lifetime (rtnladdr);
+	guint32 now;
+
+	if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT &&
+	    a_preferred == NM_PLATFORM_LIFETIME_PERMANENT)
+		return;
+
+	now = (guint32) nm_utils_get_monotonic_timestamp_s ();
+
+	if (a_preferred > a_valid)
+		a_preferred = a_valid;
+
+	if (a_valid != NM_PLATFORM_LIFETIME_PERMANENT)
+		rtnl_addr_set_valid_lifetime (rtnladdr, _get_expiry (now, a_valid));
+	rtnl_addr_set_preferred_lifetime (rtnladdr, _get_expiry (now, a_preferred));
+}
+
+/******************************************************************/
+
+/* libnl library workarounds and additions */
+
+/* Automatic deallocation of local variables */
+#define auto_nl_cache __attribute__((cleanup(put_nl_cache)))
+static void
+put_nl_cache (void *ptr)
+{
+	struct nl_cache **cache = ptr;
+
+	if (cache && *cache) {
+		nl_cache_free (*cache);
+		*cache = NULL;
+	}
+}
+
+#define auto_nl_object __attribute__((cleanup(put_nl_object)))
+static void
+put_nl_object (void *ptr)
+{
+	struct nl_object **object = ptr;
+
+	if (object && *object) {
+		nl_object_put (*object);
+		*object = NULL;
+	}
+}
+
+#define auto_nl_addr __attribute__((cleanup(put_nl_addr)))
+static void
+put_nl_addr (void *ptr)
+{
+	struct nl_addr **object = ptr;
+
+	if (object && *object) {
+		nl_addr_put (*object);
+		*object = NULL;
+	}
+}
+
+/*******************************************************************/
+
+/* wrap the libnl alloc functions and abort on out-of-memory*/
+
+static struct nl_addr *
+_nm_nl_addr_build (int family, const void *buf, size_t size)
+{
+	struct nl_addr *addr;
+
+	addr = nl_addr_build (family, (void *) buf, size);
+	if (!addr)
+		g_error ("nl_addr_build() failed with out of memory");
+
+	return addr;
+}
+
+static struct rtnl_link *
+_nm_rtnl_link_alloc (int ifindex, const char*name)
+{
+	struct rtnl_link *rtnllink;
+
+	rtnllink = rtnl_link_alloc ();
+	if (!rtnllink)
+		g_error ("rtnl_link_alloc() failed with out of memory");
+
+	if (ifindex > 0)
+		rtnl_link_set_ifindex (rtnllink, ifindex);
+	if (name)
+		rtnl_link_set_name (rtnllink, name);
+	return rtnllink;
+}
+
+static struct rtnl_addr *
+_nm_rtnl_addr_alloc (int ifindex)
+{
+	struct rtnl_addr *rtnladdr;
+
+	rtnladdr = rtnl_addr_alloc ();
+	if (!rtnladdr)
+		g_error ("rtnl_addr_alloc() failed with out of memory");
+	if (ifindex > 0)
+		rtnl_addr_set_ifindex (rtnladdr, ifindex);
+	return rtnladdr;
+}
+
+static struct rtnl_route *
+_nm_rtnl_route_alloc ()
+{
+	struct rtnl_route *rtnlroute = rtnl_route_alloc ();
+
+	if (!rtnlroute)
+		g_error ("rtnl_route_alloc() failed with out of memory");
+	return rtnlroute;
+}
+
+static struct rtnl_nexthop *
+_nm_rtnl_route_nh_alloc ()
+{
+	struct rtnl_nexthop *nexthop;
+
+	nexthop = rtnl_route_nh_alloc ();
+	if (!nexthop)
+		g_error ("rtnl_route_nh_alloc () failed with out of memory");
+	return nexthop;
+}
+
+/*******************************************************************/
+
+/* rtnl_addr_set_prefixlen fails to update the nl_addr prefixlen */
+static void
+nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen)
+{
+	struct nl_addr *nladdr;
+
+	rtnl_addr_set_prefixlen (rtnladdr, plen);
+
+	nladdr = rtnl_addr_get_local (rtnladdr);
+	if (nladdr)
+		nl_addr_set_prefixlen (nladdr, plen);
+}
+#define rtnl_addr_set_prefixlen nm_rtnl_addr_set_prefixlen
+
+typedef enum {
+	OBJECT_TYPE_UNKNOWN,
+	OBJECT_TYPE_LINK,
+	OBJECT_TYPE_IP4_ADDRESS,
+	OBJECT_TYPE_IP6_ADDRESS,
+	OBJECT_TYPE_IP4_ROUTE,
+	OBJECT_TYPE_IP6_ROUTE,
+	__OBJECT_TYPE_LAST,
+} ObjectType;
+
+static ObjectType
+object_type_from_nl_object (const struct nl_object *object)
+{
+	const char *type_str;
+
+	if (!object || !(type_str = nl_object_get_type (object)))
+		return OBJECT_TYPE_UNKNOWN;
+
+	if (!strcmp (type_str, "route/link"))
+		return OBJECT_TYPE_LINK;
+	else if (!strcmp (type_str, "route/addr")) {
+		switch (rtnl_addr_get_family ((struct rtnl_addr *) object)) {
+		case AF_INET:
+			return OBJECT_TYPE_IP4_ADDRESS;
+		case AF_INET6:
+			return OBJECT_TYPE_IP6_ADDRESS;
+		default:
+			return OBJECT_TYPE_UNKNOWN;
+		}
+	} else if (!strcmp (type_str, "route/route")) {
+		switch (rtnl_route_get_family ((struct rtnl_route *) object)) {
+		case AF_INET:
+			return OBJECT_TYPE_IP4_ROUTE;
+		case AF_INET6:
+			return OBJECT_TYPE_IP6_ROUTE;
+		default:
+			return OBJECT_TYPE_UNKNOWN;
+		}
+	} else
+		return OBJECT_TYPE_UNKNOWN;
+}
+
+static void
+_nl_link_family_unset (struct nl_object *obj, int *family)
+{
+	if (!obj || object_type_from_nl_object (obj) != OBJECT_TYPE_LINK)
+		*family = AF_UNSPEC;
+	else {
+		*family = rtnl_link_get_family ((struct rtnl_link *) obj);
+
+		/* Always explicitly set the family to AF_UNSPEC, even if rtnl_link_get_family() might
+		 * already return %AF_UNSPEC. The reason is, that %AF_UNSPEC is the default family
+		 * and libnl nl_object_identical() function will only succeed, if the family is
+		 * explicitly set (which we cannot be sure, unless setting it). */
+		rtnl_link_set_family ((struct rtnl_link *) obj, AF_UNSPEC);
+	}
+}
+
+/* In our link cache, we coerce the family of all link objects to AF_UNSPEC.
+ * Thus, before searching for an object, we fixup @needle to have the right
+ * id (by resetting the family). */
+static struct nl_object *
+nm_nl_cache_search (struct nl_cache *cache, struct nl_object *needle)
+{
+	int family;
+	struct nl_object *obj;
+
+	_nl_link_family_unset (needle, &family);
+	obj = nl_cache_search (cache, needle);
+	if (family != AF_UNSPEC) {
+		/* restore the family of the @needle instance. If the family was
+		 * unset before, we cannot make it unset again. Thus, in that case
+		 * we cannot undo _nl_link_family_unset() entirely. */
+		rtnl_link_set_family ((struct rtnl_link *) needle, family);
+	}
+
+	return obj;
+}
+
+/* Ask the kernel for an object identical (as in nl_cache_identical) to the
+ * needle argument. This is a kernel counterpart for nl_cache_search.
+ *
+ * The returned object must be freed by the caller with nl_object_put().
+ */
+static struct nl_object *
+get_kernel_object (struct nl_sock *sock, struct nl_object *needle)
+{
+	struct nl_object *object = NULL;
+	ObjectType type = object_type_from_nl_object (needle);
+
+	switch (type) {
+	case OBJECT_TYPE_LINK:
+		{
+			int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) needle);
+			const char *name = rtnl_link_get_name ((struct rtnl_link *) needle);
+			int nle;
+
+			nle = rtnl_link_get_kernel (sock, ifindex, name, (struct rtnl_link **) &object);
+			switch (nle) {
+			case -NLE_SUCCESS:
+				if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+					name = rtnl_link_get_name ((struct rtnl_link *) object);
+					debug ("get_kernel_object for link: %s (%d, family %d)",
+					       name ? name : "(unknown)",
+					       rtnl_link_get_ifindex ((struct rtnl_link *) object),
+					       rtnl_link_get_family ((struct rtnl_link *) object));
+				}
+
+				_nl_link_family_unset (object, &nle);
+				return object;
+			case -NLE_NODEV:
+				debug ("get_kernel_object for link %s (%d) had no result",
+				       name ? name : "(unknown)", ifindex);
+				return NULL;
+			default:
+				error ("get_kernel_object for link %s (%d) failed: %s (%d)",
+				       name ? name : "(unknown)", ifindex, nl_geterror (nle), nle);
+				return NULL;
+			}
+		}
+	case OBJECT_TYPE_IP4_ADDRESS:
+	case OBJECT_TYPE_IP6_ADDRESS:
+	case OBJECT_TYPE_IP4_ROUTE:
+	case OBJECT_TYPE_IP6_ROUTE:
+		/* Fallback to a one-time cache allocation. */
+		{
+			struct nl_cache *cache;
+			int nle;
+
+			/* FIXME: every time we refresh *one* object, we request an
+			 * entire dump. E.g. check_cache_items() gets O(n2) complexitly. */
+
+			nle = nl_cache_alloc_and_fill (
+					nl_cache_ops_lookup (nl_object_get_type (needle)),
+					sock, &cache);
+			if (nle) {
+				error ("get_kernel_object for type %d failed: %s (%d)",
+				       type, nl_geterror (nle), nle);
+				return NULL;
+			}
+
+			object = nl_cache_search (cache, needle);
+
+			nl_cache_free (cache);
+
+			if (object && (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS))
+				_rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object);
+
+			if (object)
+				debug ("get_kernel_object for type %d returned %p", type, object);
+			else
+				debug ("get_kernel_object for type %d had no result", type);
+			return object;
+		}
+	default:
+		g_return_val_if_reached (NULL);
+		return NULL;
+	}
+}
+
+/* libnl 3.2 doesn't seem to provide such a generic way to add libnl-route objects. */
+static int
+add_kernel_object (struct nl_sock *sock, struct nl_object *object)
+{
+	switch (object_type_from_nl_object (object)) {
+	case OBJECT_TYPE_LINK:
+		return rtnl_link_add (sock, (struct rtnl_link *) object, NLM_F_CREATE);
+	case OBJECT_TYPE_IP4_ADDRESS:
+	case OBJECT_TYPE_IP6_ADDRESS:
+		return rtnl_addr_add (sock, (struct rtnl_addr *) object, NLM_F_CREATE | NLM_F_REPLACE);
+	case OBJECT_TYPE_IP4_ROUTE:
+	case OBJECT_TYPE_IP6_ROUTE:
+		return rtnl_route_add (sock, (struct rtnl_route *) object, NLM_F_CREATE | NLM_F_REPLACE);
+	default:
+		g_return_val_if_reached (-NLE_INVAL);
+		return -NLE_INVAL;
+	}
+}
+
+/* nm_rtnl_link_parse_info_data(): Re-fetches a link from the kernel
+ * and parses its IFLA_INFO_DATA using a caller-provided parser.
+ *
+ * Code is stolen from rtnl_link_get_kernel(), nl_pickup(), and link_msg_parser().
+ */
+
+typedef int (*NMNLInfoDataParser) (struct nlattr *info_data, gpointer parser_data);
+
+typedef struct {
+	NMNLInfoDataParser parser;
+	gpointer parser_data;
+} NMNLInfoDataClosure;
+
+static struct nla_policy info_data_link_policy[IFLA_MAX + 1] = {
+	[IFLA_LINKINFO] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = {
+	[IFLA_INFO_DATA] = { .type = NLA_NESTED },
+};
+
+static int
+info_data_parser (struct nl_msg *msg, void *arg)
+{
+	NMNLInfoDataClosure *closure = arg;
+	struct nlmsghdr *n = nlmsg_hdr (msg);
+	struct nlattr *tb[IFLA_MAX + 1];
+	struct nlattr *li[IFLA_INFO_MAX + 1];
+	int err;
+
+	if (!nlmsg_valid_hdr (n, sizeof (struct ifinfomsg)))
+		return -NLE_MSG_TOOSHORT;
+
+	err = nlmsg_parse (n, sizeof (struct ifinfomsg), tb, IFLA_MAX, info_data_link_policy);
+	if (err < 0)
+		return err;
+
+	if (!tb[IFLA_LINKINFO])
+		return -NLE_MISSING_ATTR;
+
+	err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], info_data_link_info_policy);
+	if (err < 0)
+		return err;
+
+	if (!li[IFLA_INFO_DATA])
+		return -NLE_MISSING_ATTR;
+
+	return closure->parser (li[IFLA_INFO_DATA], closure->parser_data);
+}
+
+static int
+nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex,
+                              NMNLInfoDataParser parser, gpointer parser_data)
+{
+	NMNLInfoDataClosure data = { .parser = parser, .parser_data = parser_data };
+	struct nl_msg *msg = NULL;
+	struct nl_cb *cb;
+	int err;
+
+	err = rtnl_link_build_get_request (ifindex, NULL, &msg);
+	if (err < 0)
+		return err;
+
+	err = nl_send_auto (sk, msg);
+	nlmsg_free (msg);
+	if (err < 0)
+		return err;
+
+	cb = nl_cb_clone (nl_socket_get_cb (sk));
+	if (cb == NULL)
+		return -NLE_NOMEM;
+	nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, info_data_parser, &data);
+
+	err = nl_recvmsgs (sk, cb);
+	nl_cb_put (cb);
+	if (err < 0)
+		return err;
+
+	nl_wait_for_ack (sk);
+	return 0;
+}
+
+/******************************************************************/
+
+static gboolean
+ethtool_get (const char *name, gpointer edata)
+{
+	struct ifreq ifr;
+	int fd;
+
+	memset (&ifr, 0, sizeof (ifr));
+	strncpy (ifr.ifr_name, name, IFNAMSIZ);
+	ifr.ifr_data = edata;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		error ("ethtool: Could not open socket.");
+		return FALSE;
+	}
+
+	if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) {
+		debug ("ethtool: Request failed: %s", strerror (errno));
+		close (fd);
+		return FALSE;
+	}
+
+	close (fd);
+	return TRUE;
+}
+
+static int
+ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *string)
+{
+	auto_g_free struct ethtool_sset_info *info = NULL;
+	auto_g_free struct ethtool_gstrings *strings = NULL;
+	guint32 len, i;
+
+	info = g_malloc0 (sizeof (*info) + sizeof (guint32));
+	info->cmd = ETHTOOL_GSSET_INFO;
+	info->reserved = 0;
+	info->sset_mask = 1ULL << stringset_id;
+
+	if (!ethtool_get (ifname, info))
+		return -1;
+	if (!info->sset_mask)
+		return -1;
+
+	len = info->data[0];
+
+	strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN);
+	strings->cmd = ETHTOOL_GSTRINGS;
+	strings->string_set = stringset_id;
+	strings->len = len;
+	if (!ethtool_get (ifname, strings))
+		return -1;
+
+	for (i = 0; i < len; i++) {
+		if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string))
+			return i;
+	}
+
+	return -1;
+}
+
+/******************************************************************/
+
+static void
+_check_support_kernel_extended_ifa_flags_init (NMLinuxPlatformPrivate *priv, struct nl_msg *msg)
+{
+	struct nlmsghdr *msg_hdr = nlmsg_hdr (msg);
+
+	g_return_if_fail (priv->support_kernel_extended_ifa_flags == 0);
+	g_return_if_fail (msg_hdr->nlmsg_type == RTM_NEWADDR);
+
+	/* the extended address flags are only set for AF_INET6 */
+	if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6)
+		return;
+
+	/* see if the nl_msg contains the IFA_FLAGS attribute. If it does,
+	 * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR
+	 * and IFA_F_NOPREFIXROUTE (they were added together).
+	 **/
+	priv->support_kernel_extended_ifa_flags =
+	    nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */)
+	    ? 1 : -1;
+}
+
+static gboolean
+check_support_kernel_extended_ifa_flags (NMPlatform *platform)
+{
+	NMLinuxPlatformPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE);
+
+	priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+
+	if (priv->support_kernel_extended_ifa_flags == 0) {
+		nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for extended IFA_FLAGS. Assume no kernel support.");
+		priv->support_kernel_extended_ifa_flags = -1;
+	}
+
+	return priv->support_kernel_extended_ifa_flags > 0;
+}
+
+
+/* Object type specific utilities */
+
+static const char *
+type_to_string (NMLinkType type)
+{
+	/* Note that this only has to support virtual types */
+	switch (type) {
+	case NM_LINK_TYPE_DUMMY:
+		return "dummy";
+	case NM_LINK_TYPE_GRE:
+		return "gre";
+	case NM_LINK_TYPE_GRETAP:
+		return "gretap";
+	case NM_LINK_TYPE_IFB:
+		return "ifb";
+	case NM_LINK_TYPE_MACVLAN:
+		return "macvlan";
+	case NM_LINK_TYPE_MACVTAP:
+		return "macvtap";
+	case NM_LINK_TYPE_TAP:
+		return "tap";
+	case NM_LINK_TYPE_TUN:
+		return "tun";
+	case NM_LINK_TYPE_VETH:
+		return "veth";
+	case NM_LINK_TYPE_VLAN:
+		return "vlan";
+	case NM_LINK_TYPE_VXLAN:
+		return "vxlan";
+	case NM_LINK_TYPE_BRIDGE:
+		return "bridge";
+	case NM_LINK_TYPE_BOND:
+		return "bond";
+	case NM_LINK_TYPE_TEAM:
+		return "team";
+	default:
+		g_warning ("Wrong type: %d", type);
+		return NULL;
+	}
+}
+
+#define return_type(t, name) \
+	G_STMT_START { \
+		if (out_name) \
+			*out_name = name; \
+		return t; \
+	} G_STMT_END
+
+static NMLinkType
+link_type_from_udev (NMPlatform *platform, int ifindex, const char *ifname, int arptype, const char **out_name)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	GUdevDevice *udev_device;
+	const char *prop, *sysfs_path;
+
+	g_assert (ifname);
+
+	udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (ifindex));
+	if (!udev_device)
+		return_type (NM_LINK_TYPE_UNKNOWN, "unknown");
+
+	if (   g_udev_device_get_property (udev_device, "ID_NM_OLPC_MESH")
+	    || g_udev_device_get_sysfs_attr (udev_device, "anycast_mask"))
+		return_type (NM_LINK_TYPE_OLPC_MESH, "olpc-mesh");
+
+	prop = g_udev_device_get_property (udev_device, "DEVTYPE");
+	sysfs_path = g_udev_device_get_sysfs_path (udev_device);
+	if (g_strcmp0 (prop, "wlan") == 0 || wifi_utils_is_wifi (ifname, sysfs_path))
+		return_type (NM_LINK_TYPE_WIFI, "wifi");
+	else if (g_strcmp0 (prop, "wwan") == 0)
+		return_type (NM_LINK_TYPE_WWAN_ETHERNET, "wwan");
+	else if (g_strcmp0 (prop, "wimax") == 0)
+		return_type (NM_LINK_TYPE_WIMAX, "wimax");
+
+	if (arptype == ARPHRD_ETHER)
+		return_type (NM_LINK_TYPE_ETHERNET, "ethernet");
+
+	return_type (NM_LINK_TYPE_UNKNOWN, "unknown");
+}
+
+static gboolean
+link_is_software (struct rtnl_link *rtnllink)
+{
+	const char *type;
+
+	/* FIXME: replace somehow with NMLinkType or nm_platform_is_software(), but
+	 * solve the infinite callstack problems that getting the type of a TUN/TAP
+	 * device causes.
+	 */
+
+	if (   rtnl_link_get_arptype (rtnllink) == ARPHRD_INFINIBAND
+	    && strchr (rtnl_link_get_name (rtnllink), '.'))
+		return TRUE;
+
+	type = rtnl_link_get_type (rtnllink);
+	if (type == NULL)
+		return FALSE;
+
+	if (!strcmp (type, "dummy") ||
+	    !strcmp (type, "gre") ||
+	    !strcmp (type, "gretap") ||
+	    !strcmp (type, "macvlan") ||
+	    !strcmp (type, "macvtap") ||
+	    !strcmp (type, "tun") ||
+	    !strcmp (type, "veth") ||
+	    !strcmp (type, "vlan") ||
+	    !strcmp (type, "vxlan") ||
+	    !strcmp (type, "bridge") ||
+	    !strcmp (type, "bond") ||
+	    !strcmp (type, "team"))
+		return TRUE;
+
+	return FALSE;
+}
+
+static const char *
+ethtool_get_driver (const char *ifname)
+{
+	struct ethtool_drvinfo drvinfo = { 0 };
+
+	g_return_val_if_fail (ifname != NULL, NULL);
+
+	drvinfo.cmd = ETHTOOL_GDRVINFO;
+	if (!ethtool_get (ifname, &drvinfo))
+		return NULL;
+
+	if (!*drvinfo.driver)
+		return NULL;
+
+	return g_intern_string (drvinfo.driver);
+}
+
+static gboolean
+link_is_announceable (NMPlatform *platform, struct rtnl_link *rtnllink)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+
+	/* Software devices are always visible outside the platform */
+	if (link_is_software (rtnllink))
+		return TRUE;
+
+	/* Hardware devices must be found by udev so rules get run and tags set */
+	if (g_hash_table_lookup (priv->udev_devices,
+	                         GINT_TO_POINTER (rtnl_link_get_ifindex (rtnllink))))
+		return TRUE;
+
+	return FALSE;
+}
+
+static NMLinkType
+link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, const char **out_name)
+{
+	const char *type;
+
+	if (!rtnllink)
+		return_type (NM_LINK_TYPE_NONE, NULL);
+
+	type = rtnl_link_get_type (rtnllink);
+
+	if (!type) {
+		int arptype = rtnl_link_get_arptype (rtnllink);
+		const char *driver;
+		const char *ifname;
+
+		if (arptype == ARPHRD_LOOPBACK)
+			return_type (NM_LINK_TYPE_LOOPBACK, "loopback");
+		else if (arptype == ARPHRD_INFINIBAND)
+			return_type (NM_LINK_TYPE_INFINIBAND, "infiniband");
+
+		ifname = rtnl_link_get_name (rtnllink);
+		if (!ifname)
+			return_type (NM_LINK_TYPE_UNKNOWN, type);
+
+		if (arptype == 256) {
+			/* Some s390 CTC-type devices report 256 for the encapsulation type
+			 * for some reason, but we need to call them Ethernet. FIXME: use
+			 * something other than interface name to detect CTC here.
+			 */
+			if (g_str_has_prefix (ifname, "ctc"))
+				return_type (NM_LINK_TYPE_ETHERNET, "ethernet");
+		}
+
+		driver = ethtool_get_driver (ifname);
+		if (!g_strcmp0 (driver, "openvswitch"))
+			return_type (NM_LINK_TYPE_OPENVSWITCH, "openvswitch");
+
+		return link_type_from_udev (platform,
+		                            rtnl_link_get_ifindex (rtnllink),
+		                            ifname,
+		                            arptype,
+		                            out_name);
+	} else if (!strcmp (type, "dummy"))
+		return_type (NM_LINK_TYPE_DUMMY, "dummy");
+	else if (!strcmp (type, "gre"))
+		return_type (NM_LINK_TYPE_GRE, "gre");
+	else if (!strcmp (type, "gretap"))
+		return_type (NM_LINK_TYPE_GRETAP, "gretap");
+	else if (!strcmp (type, "ifb"))
+		return_type (NM_LINK_TYPE_IFB, "ifb");
+	else if (!strcmp (type, "macvlan"))
+		return_type (NM_LINK_TYPE_MACVLAN, "macvlan");
+	else if (!strcmp (type, "macvtap"))
+		return_type (NM_LINK_TYPE_MACVTAP, "macvtap");
+	else if (!strcmp (type, "tun")) {
+		NMPlatformTunProperties props;
+		guint flags;
+
+		if (nm_platform_tun_get_properties (rtnl_link_get_ifindex (rtnllink), &props)) {
+			if (!g_strcmp0 (props.mode, "tap"))
+				return_type (NM_LINK_TYPE_TAP, "tap");
+			if (!g_strcmp0 (props.mode, "tun"))
+				return_type (NM_LINK_TYPE_TUN, "tun");
+		}
+		flags = rtnl_link_get_flags (rtnllink);
+
+		nm_log_dbg (LOGD_PLATFORM, "Failed to read tun properties for interface %d (link flags: %X)",
+		                           rtnl_link_get_ifindex (rtnllink), flags);
+
+		/* try guessing the type using the link flags instead... */
+		if (flags & IFF_POINTOPOINT)
+			return_type (NM_LINK_TYPE_TUN, "tun");
+		return_type (NM_LINK_TYPE_TAP, "tap");
+	} else if (!strcmp (type, "veth"))
+		return_type (NM_LINK_TYPE_VETH, "veth");
+	else if (!strcmp (type, "vlan"))
+		return_type (NM_LINK_TYPE_VLAN, "vlan");
+	else if (!strcmp (type, "vxlan"))
+		return_type (NM_LINK_TYPE_VXLAN, "vxlan");
+	else if (!strcmp (type, "bridge"))
+		return_type (NM_LINK_TYPE_BRIDGE, "bridge");
+	else if (!strcmp (type, "bond"))
+		return_type (NM_LINK_TYPE_BOND, "bond");
+	else if (!strcmp (type, "team"))
+		return_type (NM_LINK_TYPE_TEAM, "team");
+
+	return_type (NM_LINK_TYPE_UNKNOWN, type);
+}
+
+static const char *
+udev_get_driver (NMPlatform *platform, GUdevDevice *device, int ifindex)
+{
+	GUdevDevice *parent = NULL, *grandparent = NULL;
+	const char *driver, *subsys;
+
+	driver = g_udev_device_get_driver (device);
+	if (driver)
+		return driver;
+
+	/* Try the parent */
+	parent = g_udev_device_get_parent (device);
+	if (parent) {
+		driver = g_udev_device_get_driver (parent);
+		if (!driver) {
+			/* Try the grandparent if it's an ibmebus device or if the
+			 * subsys is NULL which usually indicates some sort of
+			 * platform device like a 'gadget' net interface.
+			 */
+			subsys = g_udev_device_get_subsystem (parent);
+			if (   (g_strcmp0 (subsys, "ibmebus") == 0)
+			    || (subsys == NULL)) {
+				grandparent = g_udev_device_get_parent (parent);
+				if (grandparent) {
+					driver = g_udev_device_get_driver (grandparent);
+				}
+			}
+		}
+	}
+
+	/* Intern the string so we don't have to worry about memory
+	 * management in NMPlatformLink.
+	 */
+	if (driver)
+		driver = g_intern_string (driver);
+
+	g_clear_object (&parent);
+	g_clear_object (&grandparent);
+
+	return driver;
+}
+
+static gboolean
+init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllink)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	GUdevDevice *udev_device;
+	const char *name;
+
+	g_return_val_if_fail (rtnllink, FALSE);
+
+	name = rtnl_link_get_name (rtnllink);
+	memset (info, 0, sizeof (*info));
+
+	info->ifindex = rtnl_link_get_ifindex (rtnllink);
+	if (name)
+		g_strlcpy (info->name, name, sizeof (info->name));
+	else
+		info->name[0] = '\0';
+	info->type = link_extract_type (platform, rtnllink, &info->type_name);
+	info->up = !!(rtnl_link_get_flags (rtnllink) & IFF_UP);
+	info->connected = !!(rtnl_link_get_flags (rtnllink) & IFF_LOWER_UP);
+	info->arp = !(rtnl_link_get_flags (rtnllink) & IFF_NOARP);
+	info->master = rtnl_link_get_master (rtnllink);
+	info->parent = rtnl_link_get_link (rtnllink);
+	info->mtu = rtnl_link_get_mtu (rtnllink);
+
+	udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex));
+	if (udev_device) {
+		info->driver = udev_get_driver (platform, udev_device, info->ifindex);
+		if (!info->driver)
+			info->driver = rtnl_link_get_type (rtnllink);
+		if (!info->driver)
+			info->driver = ethtool_get_driver (info->name);
+		if (!info->driver)
+			info->driver = "unknown";
+		info->udi = g_udev_device_get_sysfs_path (udev_device);
+	}
+
+	return TRUE;
+}
+
+/* Hack: Empty bridges and bonds have IFF_LOWER_UP flag and therefore they break
+ * the carrier detection. This hack makes nm-platform think they don't have the
+ * IFF_LOWER_UP flag. This seems to also apply to bonds (specifically) with all
+ * slaves down.
+ *
+ * Note: This is still a bit racy but when NetworkManager asks for enslaving a slave,
+ * nm-platform will do that synchronously and will immediately ask for both master
+ * and slave information after the enslaving request. After the synchronous call, the
+ * master carrier is already updated with the slave carrier in mind.
+ *
+ * https://bugzilla.redhat.com/show_bug.cgi?id=910348
+ */
+static void
+hack_empty_master_iff_lower_up (NMPlatform *platform, struct nl_object *object)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	struct rtnl_link *rtnllink;
+	int ifindex;
+	struct nl_object *slave;
+	const char *type;
+
+	if (!object)
+		return;
+	if (strcmp (nl_object_get_type (object), "route/link"))
+		return;
+
+	rtnllink = (struct rtnl_link *) object;
+
+	ifindex = rtnl_link_get_ifindex (rtnllink);
+
+	type = rtnl_link_get_type (rtnllink);
+	if (!type || (strcmp (type, "bridge") != 0 && strcmp (type, "bond") != 0))
+		return;
+
+	for (slave = nl_cache_get_first (priv->link_cache); slave; slave = nl_cache_get_next (slave)) {
+		struct rtnl_link *rtnlslave = (struct rtnl_link *) slave;
+		if (rtnl_link_get_master (rtnlslave) == ifindex
+				&& rtnl_link_get_flags (rtnlslave) & IFF_LOWER_UP)
+			return;
+	}
+
+	rtnl_link_unset_flags (rtnllink, IFF_LOWER_UP);
+}
+
+static guint32
+_get_remaining_time (guint32 start_timestamp, guint32 end_timestamp)
+{
+	/* Return the remaining time between @start_timestamp until @end_timestamp.
+	 *
+	 * If @end_timestamp is NM_PLATFORM_LIFETIME_PERMANENT, it returns
+	 * NM_PLATFORM_LIFETIME_PERMANENT. If @start_timestamp already passed
+	 * @end_timestamp it returns 0. Beware, NMPlatformIPAddress treats a @lifetime
+	 * of 0 as permanent.
+	 */
+	if (end_timestamp == NM_PLATFORM_LIFETIME_PERMANENT)
+		return NM_PLATFORM_LIFETIME_PERMANENT;
+	if (start_timestamp >= end_timestamp)
+		return 0;
+	return end_timestamp - start_timestamp;
+}
+
+/* _timestamp_nl_to_ms:
+ * @timestamp_nl: a timestamp from ifa_cacheinfo.
+ * @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current
+ * uptime and how often timestamp_nl wrapped.
+ *
+ * Convert the timestamp from ifa_cacheinfo to CLOCK_MONOTONIC milliseconds.
+ * The ifa_cacheinfo fields tstamp and cstamp contains timestamps that counts
+ * with in 1/100th of a second of clock_gettime(CLOCK_MONOTONIC). However,
+ * the uint32 counter wraps every 497 days of uptime, so we have to compensate
+ * for that. */
+static gint64
+_timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms)
+{
+	const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100);
+	gint64 timestamp_nl_ms;
+
+	/* convert timestamp from 1/100th of a second to msec. */
+	timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100);
+
+	/* timestamp wraps every 497 days. Try to compensate for that.*/
+	if (timestamp_nl_ms > monotonic_ms) {
+		/* timestamp_nl_ms is in the future. Truncate it to *now* */
+		timestamp_nl_ms = monotonic_ms;
+	} else if (monotonic_ms >= WRAP_INTERVAL) {
+		timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL;
+		if (timestamp_nl_ms > monotonic_ms)
+			timestamp_nl_ms -= WRAP_INTERVAL;
+	}
+
+	return timestamp_nl_ms;
+}
+
+static guint32
+_rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr)
+{
+	guint32 last_update_time = rtnl_addr_get_last_update_time ((struct rtnl_addr *) rtnladdr);
+	struct timespec tp;
+	gint64 now_nl, now_nm, result;
+
+	/* timestamp is unset. Default to 1. */
+	if (!last_update_time)
+		return 1;
+
+	/* do all the calculations in milliseconds scale */
+
+	clock_gettime (CLOCK_MONOTONIC, &tp);
+	now_nm = nm_utils_get_monotonic_timestamp_ms ();
+	now_nl = (((gint64) tp.tv_sec) * ((gint64) 1000)) +
+	         (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000));
+
+	result = now_nm - (now_nl - _timestamp_nl_to_ms (last_update_time, now_nl));
+
+	/* converting the last_update_time into nm_utils_get_monotonic_timestamp_ms() scale is
+	 * a good guess but fails in the following situations:
+	 *
+	 * - If the address existed before start of the process, the timestamp in nm scale would
+	 *   be negative or zero. In this case we default to 1.
+	 * - during hibernation, the CLOCK_MONOTONIC/last_update_time drifts from
+	 *   nm_utils_get_monotonic_timestamp_ms() scale.
+	 */
+	if (result <= 1000)
+		return 1;
+
+	if (result > now_nm)
+		return now_nm / 1000;
+
+	return result / 1000;
+}
+
+static void
+_init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr *rtnladdr)
+{
+	guint32 a_valid = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr);
+	guint32 a_preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr);
+
+	/* the meaning of the valid and preferred lifetimes is different from the
+	 * original meaning. See _rtnl_addr_hack_lifetimes_rel_to_abs().
+	 * Beware: this function expects hacked rtnl_addr objects.
+	 */
+
+	if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT &&
+	    a_preferred == NM_PLATFORM_LIFETIME_PERMANENT) {
+		address->timestamp = 0;
+		address->lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
+		address->preferred = NM_PLATFORM_LIFETIME_PERMANENT;
+		return;
+	}
+
+	/* The valies are hacked and absolute expiry times. They must
+	 * be positive and preferred<=valid. */
+	g_assert (a_preferred <= a_valid &&
+	          a_valid > 0 &&
+	          a_preferred > 0);
+
+	if (a_valid <= 1) {
+		/* Since we want to have positive @timestamp and @valid != 0,
+		 * we must handle this case special. */
+		address->timestamp = 1;
+		address->lifetime = 1; /* Extend the lifetime by one second */
+		address->preferred = 0; /* no longer preferred. */
+		return;
+	}
+
+	/* _rtnl_addr_last_update_time_to_nm() might be wrong, so don't rely on
+	 * timestamp to have any meaning beyond anchoring the relative durations
+	 * @lifetime and @preferred.
+	 */
+	address->timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr);
+
+	/* We would expect @timestamp to be less then @a_valid. Just to be sure,
+	 * fix it up. */
+	address->timestamp = MIN (address->timestamp, a_valid - 1);
+	address->lifetime = _get_remaining_time (address->timestamp, a_valid);
+	address->preferred = _get_remaining_time (address->timestamp, a_preferred);
+}
+
+static gboolean
+init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr)
+{
+	struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr);
+	struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr);
+	const char *label;
+
+	g_return_val_if_fail (nladdr, FALSE);
+
+	memset (address, 0, sizeof (*address));
+
+	address->source = NM_PLATFORM_SOURCE_KERNEL;
+	address->ifindex = rtnl_addr_get_ifindex (rtnladdr);
+	address->plen = rtnl_addr_get_prefixlen (rtnladdr);
+	_init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr);
+	if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) {
+		g_return_val_if_reached (FALSE);
+		return FALSE;
+	}
+	memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address));
+	if (nlpeer) {
+		if (nl_addr_get_len (nlpeer) != sizeof (address->peer_address)) {
+			g_return_val_if_reached (FALSE);
+			return FALSE;
+		}
+		memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address));
+	}
+	label = rtnl_addr_get_label (rtnladdr);
+	/* Check for ':'; we're only interested in labels used as interface aliases */
+	if (label && strchr (label, ':'))
+		g_strlcpy (address->label, label, sizeof (address->label));
+
+	return TRUE;
+}
+
+static gboolean
+init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr)
+{
+	struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr);
+	struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr);
+
+	memset (address, 0, sizeof (*address));
+
+	address->source = NM_PLATFORM_SOURCE_KERNEL;
+	address->ifindex = rtnl_addr_get_ifindex (rtnladdr);
+	address->plen = rtnl_addr_get_prefixlen (rtnladdr);
+	_init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr);
+	address->flags = rtnl_addr_get_flags (rtnladdr);
+	if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) {
+		g_return_val_if_reached (FALSE);
+		return FALSE;
+	}
+	memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address));
+	if (nlpeer) {
+		if (nl_addr_get_len (nlpeer) != sizeof (address->peer_address)) {
+			g_return_val_if_reached (FALSE);
+			return FALSE;
+		}
+		memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address));
+	}
+
+	return TRUE;
+}
+
+static guint
+source_to_rtprot (NMPlatformSource source)
+{
+	switch (source) {
+	case NM_PLATFORM_SOURCE_UNKNOWN:
+		return RTPROT_UNSPEC;
+	case NM_PLATFORM_SOURCE_KERNEL:
+		return RTPROT_KERNEL;
+	case NM_PLATFORM_SOURCE_DHCP:
+		return RTPROT_DHCP;
+	case NM_PLATFORM_SOURCE_RDISC:
+		return RTPROT_RA;
+
+	default:
+		return RTPROT_STATIC;
+	}
+}
+
+static NMPlatformSource
+rtprot_to_source (guint rtprot)
+{
+	switch (rtprot) {
+	case RTPROT_UNSPEC:
+		return NM_PLATFORM_SOURCE_UNKNOWN;
+	case RTPROT_REDIRECT:
+	case RTPROT_KERNEL:
+		return NM_PLATFORM_SOURCE_KERNEL;
+	case RTPROT_RA:
+		return NM_PLATFORM_SOURCE_RDISC;
+	case RTPROT_DHCP:
+		return NM_PLATFORM_SOURCE_DHCP;
+
+	default:
+		return NM_PLATFORM_SOURCE_USER;
+	}
+}
+
+static gboolean
+init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute)
+{
+	struct nl_addr *dst, *gw;
+	struct rtnl_nexthop *nexthop;
+
+	memset (route, 0, sizeof (*route));
+
+	/* Multi-hop routes not supported. */
+	if (rtnl_route_get_nnexthops (rtnlroute) != 1)
+		return FALSE;
+
+	nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
+	dst = rtnl_route_get_dst (rtnlroute);
+	gw = rtnl_route_nh_get_gateway (nexthop);
+
+	route->ifindex = rtnl_route_nh_get_ifindex (nexthop);
+	route->plen = nl_addr_get_prefixlen (dst);
+	/* Workaround on previous workaround for libnl default route prefixlen bug. */
+	if (nl_addr_get_len (dst)) {
+		if (nl_addr_get_len (dst) != sizeof (route->network)) {
+			g_return_val_if_reached (FALSE);
+			return FALSE;
+		}
+		memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network));
+	}
+	if (gw) {
+		if (nl_addr_get_len (gw) != sizeof (route->network)) {
+			g_return_val_if_reached (FALSE);
+			return FALSE;
+		}
+		memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway));
+	}
+	route->metric = rtnl_route_get_priority (rtnlroute);
+	rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss);
+	route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute));
+
+	return TRUE;
+}
+
+static gboolean
+init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute)
+{
+	struct nl_addr *dst, *gw;
+	struct rtnl_nexthop *nexthop;
+
+	memset (route, 0, sizeof (*route));
+
+	/* Multi-hop routes not supported. */
+	if (rtnl_route_get_nnexthops (rtnlroute) != 1)
+		return FALSE;
+
+	nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
+	dst = rtnl_route_get_dst (rtnlroute);
+	gw = rtnl_route_nh_get_gateway (nexthop);
+
+	route->ifindex = rtnl_route_nh_get_ifindex (nexthop);
+	route->plen = nl_addr_get_prefixlen (dst);
+	/* Workaround on previous workaround for libnl default route prefixlen bug. */
+	if (nl_addr_get_len (dst)) {
+		if (nl_addr_get_len (dst) != sizeof (route->network)) {
+			g_return_val_if_reached (FALSE);
+			return FALSE;
+		}
+		memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network));
+	}
+	if (gw) {
+		if (nl_addr_get_len (gw) != sizeof (route->network)) {
+			g_return_val_if_reached (FALSE);
+			return FALSE;
+		}
+		memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway));
+	}
+	route->metric = rtnl_route_get_priority (rtnlroute);
+	rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss);
+	route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute));
+
+	return TRUE;
+}
+
+static char to_string_buffer[255];
+
+#define SET_AND_RETURN_STRING_BUFFER(...) \
+	G_STMT_START { \
+		g_snprintf (to_string_buffer, sizeof (to_string_buffer), ## __VA_ARGS__); \
+		g_return_val_if_reached (to_string_buffer); \
+		return to_string_buffer; \
+	} G_STMT_END
+
+static const char *
+to_string_link (NMPlatform *platform, struct rtnl_link *obj)
+{
+	NMPlatformLink pl_obj;
+
+	if (init_link (platform, &pl_obj, obj))
+		return nm_platform_link_to_string (&pl_obj);
+	SET_AND_RETURN_STRING_BUFFER ("(invalid link %p)", obj);
+}
+
+static const char *
+to_string_ip4_address (struct rtnl_addr *obj)
+{
+	NMPlatformIP4Address pl_obj;
+
+	if (init_ip4_address (&pl_obj, obj))
+		return nm_platform_ip4_address_to_string (&pl_obj);
+	SET_AND_RETURN_STRING_BUFFER ("(invalid ip4 address %p)", obj);
+}
+
+static const char *
+to_string_ip6_address (struct rtnl_addr *obj)
+{
+	NMPlatformIP6Address pl_obj;
+
+	if (init_ip6_address (&pl_obj, obj))
+		return nm_platform_ip6_address_to_string (&pl_obj);
+	SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 address %p)", obj);
+}
+
+static const char *
+to_string_ip4_route (struct rtnl_route *obj)
+{
+	NMPlatformIP4Route pl_obj;
+
+	if (init_ip4_route (&pl_obj, obj))
+		return nm_platform_ip4_route_to_string (&pl_obj);
+	SET_AND_RETURN_STRING_BUFFER ("(invalid ip4 route %p)", obj);
+}
+
+static const char *
+to_string_ip6_route (struct rtnl_route *obj)
+{
+	NMPlatformIP6Route pl_obj;
+
+	if (init_ip6_route (&pl_obj, obj))
+		return nm_platform_ip6_route_to_string (&pl_obj);
+	SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 route %p)", obj);
+}
+
+static const char *
+to_string_object_with_type (NMPlatform *platform, struct nl_object *obj, ObjectType type)
+{
+	switch (type) {
+	case OBJECT_TYPE_LINK:
+		return to_string_link (platform, (struct rtnl_link *) obj);
+	case OBJECT_TYPE_IP4_ADDRESS:
+		return to_string_ip4_address ((struct rtnl_addr *) obj);
+	case OBJECT_TYPE_IP6_ADDRESS:
+		return to_string_ip6_address ((struct rtnl_addr *) obj);
+	case OBJECT_TYPE_IP4_ROUTE:
+		return to_string_ip4_route ((struct rtnl_route *) obj);
+	case OBJECT_TYPE_IP6_ROUTE:
+		return to_string_ip6_route ((struct rtnl_route *) obj);
+	default:
+		SET_AND_RETURN_STRING_BUFFER ("(unknown netlink object %p)", obj);
+	}
+}
+
+static const char *
+to_string_object (NMPlatform *platform, struct nl_object *obj)
+{
+	return to_string_object_with_type (platform, obj, object_type_from_nl_object (obj));
+}
+
+#undef SET_AND_RETURN_STRING_BUFFER
+
+/******************************************************************/
+
+/* Object and cache manipulation */
+
+static const char *signal_by_type_and_status[__OBJECT_TYPE_LAST] = {
+	[OBJECT_TYPE_LINK]        = NM_PLATFORM_SIGNAL_LINK_CHANGED,
+	[OBJECT_TYPE_IP4_ADDRESS] = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED,
+	[OBJECT_TYPE_IP6_ADDRESS] = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
+	[OBJECT_TYPE_IP4_ROUTE]   = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED,
+	[OBJECT_TYPE_IP6_ROUTE]   = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED,
+};
+
+static struct nl_cache *
+choose_cache_by_type (NMPlatform *platform, ObjectType object_type)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+
+	switch (object_type) {
+	case OBJECT_TYPE_LINK:
+		return priv->link_cache;
+	case OBJECT_TYPE_IP4_ADDRESS:
+	case OBJECT_TYPE_IP6_ADDRESS:
+		return priv->address_cache;
+	case OBJECT_TYPE_IP4_ROUTE:
+	case OBJECT_TYPE_IP6_ROUTE:
+		return priv->route_cache;
+	default:
+		g_return_val_if_reached (NULL);
+		return NULL;
+	}
+}
+
+static struct nl_cache *
+choose_cache (NMPlatform *platform, struct nl_object *object)
+{
+	return choose_cache_by_type (platform, object_type_from_nl_object (object));
+}
+
+static gboolean
+object_has_ifindex (struct nl_object *object, int ifindex)
+{
+	switch (object_type_from_nl_object (object)) {
+	case OBJECT_TYPE_IP4_ADDRESS:
+	case OBJECT_TYPE_IP6_ADDRESS:
+		return ifindex == rtnl_addr_get_ifindex ((struct rtnl_addr *) object);
+	case OBJECT_TYPE_IP4_ROUTE:
+	case OBJECT_TYPE_IP6_ROUTE:
+		{
+			struct rtnl_route *rtnlroute = (struct rtnl_route *) object;
+			struct rtnl_nexthop *nexthop;
+
+			if (rtnl_route_get_nnexthops (rtnlroute) != 1)
+				return FALSE;
+			nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
+
+			return ifindex == rtnl_route_nh_get_ifindex (nexthop);
+		}
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+static gboolean refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason);
+
+static void
+check_cache_items (NMPlatform *platform, struct nl_cache *cache, int ifindex)
+{
+	auto_nl_cache struct nl_cache *cloned_cache = nl_cache_clone (cache);
+	struct nl_object *object;
+	GPtrArray *objects_to_refresh = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put);
+	guint i;
+
+	for (object = nl_cache_get_first (cloned_cache); object; object = nl_cache_get_next (object)) {
+		if (object_has_ifindex (object, ifindex)) {
+			nl_object_get (object);
+			g_ptr_array_add (objects_to_refresh, object);
+		}
+	}
+
+	for (i = 0; i < objects_to_refresh->len; i++)
+		refresh_object (platform, objects_to_refresh->pdata[i], TRUE, NM_PLATFORM_REASON_CACHE_CHECK);
+
+	g_ptr_array_free (objects_to_refresh, TRUE);
+}
+
+static void
+announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatformSignalChangeType change_type, NMPlatformReason reason)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	ObjectType object_type = object_type_from_nl_object (object);
+	const char *sig = signal_by_type_and_status[object_type];
+
+	switch (object_type) {
+	case OBJECT_TYPE_LINK:
+		{
+			NMPlatformLink device;
+			struct rtnl_link *rtnl_link = (struct rtnl_link *) object;
+
+			if (!init_link (platform, &device, rtnl_link))
+				return;
+
+			/* Skip hardware devices not yet discovered by udev. They will be
+			 * announced by udev_device_added(). This doesn't apply to removed
+			 * devices, as those come either from udev_device_removed(),
+			 * event_notification() or link_delete() which block the announcment
+			 * themselves when appropriate.
+			 */
+			switch (change_type) {
+			case NM_PLATFORM_SIGNAL_ADDED:
+			case NM_PLATFORM_SIGNAL_CHANGED:
+				if (!link_is_software (rtnl_link) && !device.driver)
+					return;
+				break;
+			default:
+				break;
+			}
+
+			/* Link deletion or setting down is sometimes accompanied by address
+			 * and/or route deletion.
+			 *
+			 * More precisely, kernel removes routes when interface goes !IFF_UP and
+			 * removes both addresses and routes when interface is removed.
+			 */
+			switch (change_type) {
+			case NM_PLATFORM_SIGNAL_CHANGED:
+				if (!device.connected)
+					check_cache_items (platform, priv->route_cache, device.ifindex);
+				break;
+			case NM_PLATFORM_SIGNAL_REMOVED:
+				check_cache_items (platform, priv->address_cache, device.ifindex);
+				check_cache_items (platform, priv->route_cache, device.ifindex);
+				g_hash_table_remove (priv->wifi_data, GINT_TO_POINTER (device.ifindex));
+				break;
+			default:
+				break;
+			}
+
+			g_signal_emit_by_name (platform, sig, device.ifindex, &device, change_type, reason);
+		}
+		return;
+	case OBJECT_TYPE_IP4_ADDRESS:
+		{
+			NMPlatformIP4Address address;
+
+			if (!init_ip4_address (&address, (struct rtnl_addr *) object))
+				return;
+
+			/* Address deletion is sometimes accompanied by route deletion. We need to
+			 * check all routes belonging to the same interface.
+			 */
+			switch (change_type) {
+			case NM_PLATFORM_SIGNAL_REMOVED:
+				check_cache_items (platform, priv->route_cache, address.ifindex);
+				break;
+			default:
+				break;
+			}
+
+			g_signal_emit_by_name (platform, sig, address.ifindex, &address, change_type, reason);
+		}
+		return;
+	case OBJECT_TYPE_IP6_ADDRESS:
+		{
+			NMPlatformIP6Address address;
+
+			if (!init_ip6_address (&address, (struct rtnl_addr *) object))
+				return;
+			g_signal_emit_by_name (platform, sig, address.ifindex, &address, change_type, reason);
+		}
+		return;
+	case OBJECT_TYPE_IP4_ROUTE:
+		{
+			NMPlatformIP4Route route;
+
+			if (init_ip4_route (&route, (struct rtnl_route *) object))
+				g_signal_emit_by_name (platform, sig, route.ifindex, &route, change_type, reason);
+		}
+		return;
+	case OBJECT_TYPE_IP6_ROUTE:
+		{
+			NMPlatformIP6Route route;
+
+			if (init_ip6_route (&route, (struct rtnl_route *) object))
+				g_signal_emit_by_name (platform, sig, route.ifindex, &route, change_type, reason);
+		}
+		return;
+	default:
+		g_return_if_reached ();
+	}
+}
+
+static struct nl_object * build_rtnl_link (int ifindex, const char *name, NMLinkType type);
+
+static gboolean
+refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	auto_nl_object struct nl_object *cached_object = NULL;
+	auto_nl_object struct nl_object *kernel_object = NULL;
+	struct nl_cache *cache;
+	int nle;
+
+	cache = choose_cache (platform, object);
+	cached_object = nm_nl_cache_search (cache, object);
+	kernel_object = get_kernel_object (priv->nlh, object);
+
+	if (removed) {
+		if (kernel_object)
+			return TRUE;
+
+		/* Only announce object if it was still in the cache. */
+		if (cached_object) {
+			nl_cache_remove (cached_object);
+
+			announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, reason);
+		}
+	} else {
+		if (!kernel_object)
+			return FALSE;
+
+		hack_empty_master_iff_lower_up (platform, kernel_object);
+
+		if (cached_object)
+			nl_cache_remove (cached_object);
+		nle = nl_cache_add (cache, kernel_object);
+		if (nle) {
+			nm_log_dbg (LOGD_PLATFORM, "refresh_object(reason %d) failed during nl_cache_add with %d", reason, nle);
+			return FALSE;
+		}
+
+		announce_object (platform, kernel_object, cached_object ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, reason);
+
+		/* Refresh the master device (even on enslave/release) */
+		if (object_type_from_nl_object (kernel_object) == OBJECT_TYPE_LINK) {
+			int kernel_master = rtnl_link_get_master ((struct rtnl_link *) kernel_object);
+			int cached_master = cached_object ? rtnl_link_get_master ((struct rtnl_link *) cached_object) : 0;
+			struct nl_object *master_object;
+
+			if (kernel_master) {
+				master_object = build_rtnl_link (kernel_master, NULL, NM_LINK_TYPE_NONE);
+				refresh_object (platform, master_object, FALSE, NM_PLATFORM_REASON_INTERNAL);
+				nl_object_put (master_object);
+			}
+			if (cached_master && cached_master != kernel_master) {
+				master_object = build_rtnl_link (cached_master, NULL, NM_LINK_TYPE_NONE);
+				refresh_object (platform, master_object, FALSE, NM_PLATFORM_REASON_INTERNAL);
+				nl_object_put (master_object);
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+/* Decreases the reference count if @obj for convenience */
+static gboolean
+add_object (NMPlatform *platform, struct nl_object *obj)
+{
+	auto_nl_object struct nl_object *object = obj;
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	int nle;
+	struct nl_dump_params dp = {
+		.dp_type = NL_DUMP_DETAILS,
+		.dp_fd = stderr,
+	};
+
+	g_return_val_if_fail (object, FALSE);
+
+	nle = add_kernel_object (priv->nlh, object);
+
+	/* NLE_EXIST is considered equivalent to success to avoid race conditions. You
+	 * never know when something sends an identical object just before
+	 * NetworkManager.
+	 */
+	switch (nle) {
+	case -NLE_SUCCESS:
+	case -NLE_EXIST:
+		break;
+	default:
+		error ("Netlink error adding %s: %s", to_string_object (platform, object),  nl_geterror (nle));
+		nl_object_dump (object, &dp);
+		return FALSE;
+	}
+
+	return refresh_object (platform, object, FALSE, NM_PLATFORM_REASON_INTERNAL);
+}
+
+/* Decreases the reference count if @obj for convenience */
+static gboolean
+delete_object (NMPlatform *platform, struct nl_object *obj, gboolean do_refresh_object)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	auto_nl_object struct nl_object *obj_cleanup = obj;
+	struct nl_object *object = obj;
+	int object_type;
+	int nle;
+
+	object_type = object_type_from_nl_object (obj);
+	g_return_val_if_fail (object_type != OBJECT_TYPE_UNKNOWN, FALSE);
+
+	switch (object_type) {
+	case OBJECT_TYPE_LINK:
+		nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object);
+		break;
+	case OBJECT_TYPE_IP4_ADDRESS:
+	case OBJECT_TYPE_IP6_ADDRESS:
+		nle = rtnl_addr_delete (priv->nlh, (struct rtnl_addr *) object, 0);
+		break;
+	case OBJECT_TYPE_IP4_ROUTE:
+	case OBJECT_TYPE_IP6_ROUTE:
+		nle = rtnl_route_delete (priv->nlh, (struct rtnl_route *) object, 0);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+
+	switch (nle) {
+	case -NLE_SUCCESS:
+		break;
+	case -NLE_OBJ_NOTFOUND:
+		debug("delete_object failed with \"%s\" (%d), meaning the object was already removed",
+		      nl_geterror (nle), nle);
+		break;
+	case -NLE_FAILURE:
+		if (object_type == OBJECT_TYPE_IP6_ADDRESS) {
+			/* On RHEL7 kernel, deleting a non existing address fails with ENXIO (which libnl maps to NLE_FAILURE) */
+			debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed",
+			      nl_geterror (nle), nle);
+			break;
+		}
+		goto DEFAULT;
+	case -NLE_NOADDR:
+		if (object_type == OBJECT_TYPE_IP4_ADDRESS || object_type == OBJECT_TYPE_IP6_ADDRESS) {
+			debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed",
+			      nl_geterror (nle), nle);
+			break;
+		}
+		goto DEFAULT;
+	DEFAULT:
+	default:
+		error ("Netlink error deleting %s: %s (%d)", to_string_object (platform, obj), nl_geterror (nle), nle);
+		return FALSE;
+	}
+
+	if (do_refresh_object)
+		refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_INTERNAL);
+
+	return TRUE;
+}
+
+static void
+ref_object (struct nl_object *obj, void *data)
+{
+	struct nl_object **out = data;
+
+	nl_object_get (obj);
+	*out = obj;
+}
+
+static gboolean
+_rtnl_addr_timestamps_equal_fuzzy (guint32 ts1, guint32 ts2)
+{
+	guint32 diff;
+
+	if (ts1 == ts2)
+		return TRUE;
+	if (ts1 == NM_PLATFORM_LIFETIME_PERMANENT ||
+	    ts2 == NM_PLATFORM_LIFETIME_PERMANENT)
+		return FALSE;
+
+	/** accept the timestamps as equal if they are within two seconds. */
+	diff = ts1 > ts2 ? ts1 - ts2 : ts2 - ts1;
+	return diff <= 2;
+}
+
+/* This function does all the magic to avoid race conditions caused
+ * by concurrent usage of synchronous commands and an asynchronous cache. This
+ * might be a nice future addition to libnl but it requires to do all operations
+ * through the cache manager. In this case, nm-linux-platform serves as the
+ * cache manager instead of the one provided by libnl.
+ */
+static int
+event_notification (struct nl_msg *msg, gpointer user_data)
+{
+	NMPlatform *platform = NM_PLATFORM (user_data);
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	struct nl_cache *cache;
+	auto_nl_object struct nl_object *object = NULL;
+	auto_nl_object struct nl_object *cached_object = NULL;
+	auto_nl_object struct nl_object *kernel_object = NULL;
+	int event;
+	int nle;
+	ObjectType type;
+
+	event = nlmsg_hdr (msg)->nlmsg_type;
+
+	if (priv->support_kernel_extended_ifa_flags == 0 && event == RTM_NEWADDR) {
+		/* if kernel support for extended ifa flags is still undecided, use the opportunity
+		 * now and use @msg to decide it. This saves a blocking net link request.
+		 **/
+		_check_support_kernel_extended_ifa_flags_init (priv, msg);
+	}
+
+	nl_msg_parse (msg, ref_object, &object);
+	g_return_val_if_fail (object, NL_OK);
+
+	type = object_type_from_nl_object (object);
+
+	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+		if (type == OBJECT_TYPE_LINK) {
+			const char *name = rtnl_link_get_name ((struct rtnl_link *) object);
+
+			debug ("netlink event (type %d) for link: %s (%d, family %d)",
+			       event, name ? name : "(unknown)",
+			       rtnl_link_get_ifindex ((struct rtnl_link *) object),
+			       rtnl_link_get_family ((struct rtnl_link *) object));
+		} else
+			debug ("netlink event (type %d)", event);
+	}
+
+	cache = choose_cache_by_type (platform, type);
+	cached_object = nm_nl_cache_search (cache, object);
+	kernel_object = get_kernel_object (priv->nlh, object);
+
+	hack_empty_master_iff_lower_up (platform, kernel_object);
+
+	/* Removed object */
+	switch (event) {
+	case RTM_DELLINK:
+	case RTM_DELADDR:
+	case RTM_DELROUTE:
+		/* Ignore inconsistent deletion
+		 *
+		 * Quick external deletion and addition can be occasionally
+		 * seen as just a change.
+		 */
+		if (kernel_object)
+			return NL_OK;
+		/* Ignore internal deletion */
+		if (!cached_object)
+			return NL_OK;
+
+		nl_cache_remove (cached_object);
+		/* Don't announce removed interfaces that are not recognized by
+		 * udev. They were either not yet discovered or they have been
+		 * already removed and announced.
+		 */
+		if (event == RTM_DELLINK) {
+			if (!link_is_announceable (platform, (struct rtnl_link *) cached_object))
+				return NL_OK;
+		}
+		announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL);
+
+		return NL_OK;
+	case RTM_NEWLINK:
+	case RTM_NEWADDR:
+	case RTM_NEWROUTE:
+		/* Ignore inconsistent addition or change (kernel will send a good one)
+		 *
+		 * Quick sequence of RTM_NEWLINK notifications can be occasionally
+		 * collapsed to just one addition or deletion, depending of whether we
+		 * already have the object in cache.
+		 */
+		if (!kernel_object)
+			return NL_OK;
+		/* Handle external addition */
+		if (!cached_object) {
+			nle = nl_cache_add (cache, kernel_object);
+			if (nle) {
+				error ("netlink cache error: %s", nl_geterror (nle));
+				return NL_OK;
+			}
+			announce_object (platform, kernel_object, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL);
+			return NL_OK;
+		}
+		/* Ignore non-change
+		 *
+		 * This also catches notifications for internal addition or change, unless
+		 * another action occured very soon after it.
+		 */
+		if (!nl_object_diff (kernel_object, cached_object)) {
+			if (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS) {
+				struct rtnl_addr *c = (struct rtnl_addr *) cached_object;
+				struct rtnl_addr *k = (struct rtnl_addr *) kernel_object;
+
+				/* libnl nl_object_diff() ignores differences in timestamp. Let's care about
+				 * them (if they are large enough).
+				 *
+				 * Note that these valid and preferred timestamps are absolute, after
+				 * _rtnl_addr_hack_lifetimes_rel_to_abs(). */
+				if (   _rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_preferred_lifetime (c),
+				                                          rtnl_addr_get_preferred_lifetime (k))
+				    && _rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_valid_lifetime (c),
+				                                          rtnl_addr_get_valid_lifetime (k)))
+					return NL_OK;
+			} else
+				return NL_OK;
+		}
+		/* Handle external change */
+		nl_cache_remove (cached_object);
+		nle = nl_cache_add (cache, kernel_object);
+		if (nle) {
+			error ("netlink cache error: %s", nl_geterror (nle));
+			return NL_OK;
+		}
+		announce_object (platform, kernel_object, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL);
+
+		return NL_OK;
+	default:
+		error ("Unknown netlink event: %d", event);
+		return NL_OK;
+	}
+}
+
+/******************************************************************/
+
+static void
+_log_dbg_sysctl_set_impl (const char *path, const char *value)
+{
+	GError *error = NULL;
+	char *contents, *contents_escaped;
+	char *value_escaped = g_strescape (value, NULL);
+
+	if (!g_file_get_contents (path, &contents, NULL, &error)) {
+		debug ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", path, value_escaped, error->message);
+		g_clear_error (&error);
+	} else {
+		g_strstrip (contents);
+		contents_escaped = g_strescape (contents, NULL);
+		if (strcmp (contents, value) == 0)
+			debug ("sysctl: setting '%s' to '%s' (current value is identical)", path, value_escaped);
+		else
+			debug ("sysctl: setting '%s' to '%s' (current value is '%s')", path, value_escaped, contents_escaped);
+		g_free (contents);
+		g_free (contents_escaped);
+	}
+	g_free (value_escaped);
+}
+
+#define _log_dbg_sysctl_set(path, value) \
+	G_STMT_START { \
+		if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { \
+			_log_dbg_sysctl_set_impl (path, value); \
+		} \
+	} G_STMT_END
+
+static gboolean
+sysctl_set (NMPlatform *platform, const char *path, const char *value)
+{
+	int fd, len, nwrote, tries;
+	char *actual;
+
+	g_return_val_if_fail (path != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+
+	/* Don't write outside known locations */
+	g_assert (g_str_has_prefix (path, "/proc/sys/")
+	          || g_str_has_prefix (path, "/sys/"));
+	/* Don't write to suspicious locations */
+	g_assert (!strstr (path, "/../"));
+
+	fd = open (path, O_WRONLY | O_TRUNC);
+	if (fd == -1) {
+		if (errno == ENOENT) {
+			debug ("sysctl: failed to open '%s': (%d) %s",
+			       path, errno, strerror (errno));
+		} else {
+			error ("sysctl: failed to open '%s': (%d) %s",
+			       path, errno, strerror (errno));
+		}
+		return FALSE;
+	}
+
+	_log_dbg_sysctl_set (path, value);
+
+	/* Most sysfs and sysctl options don't care about a trailing LF, while some
+	 * (like infiniband) do.  So always add the LF.  Also, neither sysfs nor
+	 * sysctl support partial writes so the LF must be added to the string we're
+	 * about to write.
+	 */
+	actual = g_strdup_printf ("%s\n", value);
+
+	/* Try to write the entire value three times if a partial write occurs */
+	len = strlen (actual);
+	for (tries = 0, nwrote = 0; tries < 3 && nwrote != len; tries++) {
+		nwrote = write (fd, actual, len);
+		if (nwrote == -1) {
+			if (errno == EINTR) {
+				debug ("sysctl: interrupted, will try again");
+				continue;
+			}
+			break;
+		}
+	}
+	if (nwrote == -1 && errno != EEXIST) {
+		error ("sysctl: failed to set '%s' to '%s': (%d) %s",
+		       path, value, errno, strerror (errno));
+	} else if (nwrote < len) {
+		error ("sysctl: failed to set '%s' to '%s' after three attempts",
+		       path, value);
+	}
+
+	g_free (actual);
+	close (fd);
+	return (nwrote == len);
+}
+
+static GHashTable *sysctl_get_prev_values;
+
+static void
+_log_dbg_sysctl_get_impl (const char *path, const char *contents)
+{
+	const char *prev_value = NULL;
+
+	if (!sysctl_get_prev_values)
+		sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+	else
+		prev_value = g_hash_table_lookup (sysctl_get_prev_values, path);
+
+	if (prev_value) {
+		if (strcmp (prev_value, contents) != 0) {
+			char *contents_escaped = g_strescape (contents, NULL);
+			char *prev_value_escaped = g_strescape (prev_value, NULL);
+
+			debug ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", path, contents_escaped, prev_value_escaped);
+			g_free (contents_escaped);
+			g_free (prev_value_escaped);
+			g_hash_table_insert (sysctl_get_prev_values, g_strdup (path), g_strdup (contents));
+		}
+	} else {
+		char *contents_escaped = g_strescape (contents, NULL);
+
+		debug ("sysctl: reading '%s': '%s'", path, contents_escaped);
+		g_free (contents_escaped);
+		g_hash_table_insert (sysctl_get_prev_values, g_strdup (path), g_strdup (contents));
+	}
+}
+
+#define _log_dbg_sysctl_get(path, contents) \
+	G_STMT_START { \
+		if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { \
+			_log_dbg_sysctl_get_impl (path, contents); \
+		} else if (sysctl_get_prev_values) { \
+			g_hash_table_destroy (sysctl_get_prev_values); \
+			sysctl_get_prev_values = NULL; \
+		} \
+	} G_STMT_END
+
+static char *
+sysctl_get (NMPlatform *platform, const char *path)
+{
+	GError *error = NULL;
+	char *contents;
+
+	/* Don't write outside known locations */
+	g_assert (g_str_has_prefix (path, "/proc/sys/")
+	          || g_str_has_prefix (path, "/sys/"));
+	/* Don't write to suspicious locations */
+	g_assert (!strstr (path, "/../"));
+
+	if (!g_file_get_contents (path, &contents, NULL, &error)) {
+		/* We assume FAILED means EOPNOTSUP */
+		if (   g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)
+		    || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED))
+			debug ("error reading %s: %s", path, error->message);
+		else
+			error ("error reading %s: %s", path, error->message);
+		g_clear_error (&error);
+		return NULL;
+	}
+
+	g_strstrip (contents);
+
+	_log_dbg_sysctl_get (path, contents);
+
+	return contents;
+}
+
+/******************************************************************/
+
+static GArray *
+link_get_all (NMPlatform *platform)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	GArray *links = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformLink), nl_cache_nitems (priv->link_cache));
+	NMPlatformLink device;
+	struct nl_object *object;
+
+	for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) {
+		struct rtnl_link *rtnl_link = (struct rtnl_link *) object;
+
+		if (link_is_announceable (platform, rtnl_link)) {
+			if (init_link (platform, &device, rtnl_link))
+				g_array_append_val (links, device);
+		}
+	}
+
+	return links;
+}
+
+static gboolean
+_nm_platform_link_get (NMPlatform *platform, int ifindex, NMPlatformLink *link)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	auto_nl_object struct rtnl_link *rtnllink = NULL;
+
+	rtnllink = rtnl_link_get (priv->link_cache, ifindex);
+	if (rtnllink) {
+		if (link_is_announceable (platform, rtnllink)) {
+			if (init_link (platform, link, rtnllink))
+				return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static struct nl_object *
+build_rtnl_link (int ifindex, const char *name, NMLinkType type)
+{
+	struct rtnl_link *rtnllink;
+	int nle;
+
+	rtnllink = _nm_rtnl_link_alloc (ifindex, name);
+	if (type) {
+		nle = rtnl_link_set_type (rtnllink, type_to_string (type));
+		g_assert (!nle);
+	}
+	return (struct nl_object *) rtnllink;
+}
+
+static gboolean
+link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len)
+{
+	int r;
+	struct nl_object *link;
+
+	if (type == NM_LINK_TYPE_BOND) {
+		/* When the kernel loads the bond module, either via explicit modprobe
+		 * or automatically in response to creating a bond master, it will also
+		 * create a 'bond0' interface.  Since the bond we're about to create may
+		 * or may not be named 'bond0' prevent potential confusion about a bond
+		 * that the user didn't want by telling the bonding module not to create
+		 * bond0 automatically.
+		 */
+		if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS))
+			/* Ignore return value to shut up the compiler */
+			r = system ("modprobe bonding max_bonds=0");
+	}
+
+	debug ("link: add link '%s' of type '%s' (%d)",
+	       name, type_to_string (type), (int) type);
+
+	link = build_rtnl_link (0, name, type);
+
+	g_assert ( (address != NULL) ^ (address_len == 0) );
+	if (address) {
+		auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, address_len);
+
+		rtnl_link_set_addr ((struct rtnl_link *) link, nladdr);
+	}
+	return add_object (platform, link);
+}
+
+static struct rtnl_link *
+link_get (NMPlatform *platform, int ifindex)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex);
+
+	if (!rtnllink) {
+		platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
+		return NULL;
+	}
+
+	/* physical interfaces must be found by udev before they can be used */
+	if (!link_is_announceable (platform, rtnllink)) {
+		platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
+		rtnl_link_put (rtnllink);
+		return NULL;
+	}
+
+	return rtnllink;
+}
+
+static gboolean
+link_change (NMPlatform *platform, int ifindex, struct rtnl_link *change)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
+	int nle;
+
+	if (!rtnllink)
+		return FALSE;
+	g_return_val_if_fail (rtnl_link_get_ifindex (change) > 0, FALSE);
+
+	nle = rtnl_link_change (priv->nlh, rtnllink, change, 0);
+
+	/* NLE_EXIST is considered equivalent to success to avoid race conditions. You
+	 * never know when something sends an identical object just before
+	 * NetworkManager.
+	 *
+	 * When netlink returns NLE_OBJ_NOTFOUND, it usually means it failed to find
+	 * firmware for the device, especially on nm_platform_link_set_up ().
+	 * This is basically the same check as in the original code and could
+	 * potentially be improved.
+	 */
+	switch (nle) {
+	case -NLE_SUCCESS:
+	case -NLE_EXIST:
+		break;
+	case -NLE_OBJ_NOTFOUND:
+		error ("Firmware not found for changing link %s; Netlink error: %s)", to_string_link (platform, change), nl_geterror (nle));
+		platform->error = NM_PLATFORM_ERROR_NO_FIRMWARE;
+		return FALSE;
+	default:
+		error ("Netlink error changing link %s: %s", to_string_link (platform, change), nl_geterror (nle));
+		return FALSE;
+	}
+
+	return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL);
+}
+
+static gboolean
+link_delete (NMPlatform *platform, int ifindex)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex);
+
+	if (!rtnllink) {
+		platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
+		return FALSE;
+	}
+
+	return delete_object (platform, build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_NONE), TRUE);
+}
+
+static int
+link_get_ifindex (NMPlatform *platform, const char *ifname)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+
+	return rtnl_link_name2i (priv->link_cache, ifname);
+}
+
+static const char *
+link_get_name (NMPlatform *platform, int ifindex)
+{
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
+
+	return rtnllink ? rtnl_link_get_name (rtnllink) : NULL;
+}
+
+static NMLinkType
+link_get_type (NMPlatform *platform, int ifindex)
+{
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
+
+	return link_extract_type (platform, rtnllink, NULL);
+}
+
+static const char *
+link_get_type_name (NMPlatform *platform, int ifindex)
+{
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
+	const char *type;
+
+	link_extract_type (platform, rtnllink, &type);
+	return type;
+}
+
+static guint32
+link_get_flags (NMPlatform *platform, int ifindex)
+{
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
+
+	if (!rtnllink)
+		return IFF_NOARP;
+
+	return rtnl_link_get_flags (rtnllink);
+}
+
+static gboolean
+link_refresh (NMPlatform *platform, int ifindex)
+{
+	auto_nl_object struct rtnl_link *rtnllink = _nm_rtnl_link_alloc (ifindex, NULL);
+
+	return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_EXTERNAL);
+}
+
+static gboolean
+link_is_up (NMPlatform *platform, int ifindex)
+{
+	return !!(link_get_flags (platform, ifindex) & IFF_UP);
+}
+
+static gboolean
+link_is_connected (NMPlatform *platform, int ifindex)
+{
+	return !!(link_get_flags (platform, ifindex) & IFF_LOWER_UP);
+}
+
+static gboolean
+link_uses_arp (NMPlatform *platform, int ifindex)
+{
+	return !(link_get_flags (platform, ifindex) & IFF_NOARP);
+}
+
+static gboolean
+link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboolean value)
+{
+	auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL);
+
+	if (value)
+		rtnl_link_set_flags (change, flags);
+	else
+		rtnl_link_unset_flags (change, flags);
+
+	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+		char buf[512];
+
+		rtnl_link_flags2str (flags, buf, sizeof (buf));
+		debug ("link: change %d: flags %s '%s' (%d)", ifindex, value ? "set" : "unset", buf, flags);
+	}
+
+	return link_change (platform, ifindex, change);
+}
+
+static gboolean
+link_set_up (NMPlatform *platform, int ifindex)
+{
+	return link_change_flags (platform, ifindex, IFF_UP, TRUE);
+}
+
+static gboolean
+link_set_down (NMPlatform *platform, int ifindex)
+{
+	return link_change_flags (platform, ifindex, IFF_UP, FALSE);
+}
+
+static gboolean
+link_set_arp (NMPlatform *platform, int ifindex)
+{
+	return link_change_flags (platform, ifindex, IFF_NOARP, FALSE);
+}
+
+static gboolean
+link_set_noarp (NMPlatform *platform, int ifindex)
+{
+	return link_change_flags (platform, ifindex, IFF_NOARP, TRUE);
+}
+
+static gboolean
+supports_ethtool_carrier_detect (const char *ifname)
+{
+	struct ethtool_cmd edata = { .cmd = ETHTOOL_GLINK };
+
+	/* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we
+	 * assume the device supports carrier-detect, otherwise we assume it
+	 * doesn't.
+	 */
+	return ethtool_get (ifname, &edata);
+}
+
+static gboolean
+supports_mii_carrier_detect (const char *ifname)
+{
+	int fd;
+	struct ifreq ifr;
+	struct mii_ioctl_data *mii;
+	gboolean supports_mii = FALSE;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_PLATFORM, "couldn't open control socket.");
+		return FALSE;
+	}
+
+	memset (&ifr, 0, sizeof (struct ifreq));
+	strncpy (ifr.ifr_name, ifname, IFNAMSIZ);
+
+	errno = 0;
+	if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) {
+		nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIPHY failed: %d", errno);
+		goto out;
+	}
+
+	/* If we can read the BMSR register, we assume that the card supports MII link detection */
+	mii = (struct mii_ioctl_data *) &ifr.ifr_ifru;
+	mii->reg_num = MII_BMSR;
+
+	if (ioctl (fd, SIOCGMIIREG, &ifr) == 0) {
+		nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG result 0x%X", mii->val_out);
+		supports_mii = TRUE;
+	} else {
+		nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG failed: %d", errno);
+	}
+
+ out:
+	close (fd);
+	nm_log_dbg (LOGD_PLATFORM, "MII %s supported", supports_mii ? "is" : "not");
+	return supports_mii;	
+}
+
+static gboolean
+link_supports_carrier_detect (NMPlatform *platform, int ifindex)
+{
+	const char *name = nm_platform_link_get_name (ifindex);
+
+	if (!name)
+		return FALSE;
+
+	/* We use netlink for the actual carrier detection, but netlink can't tell
+	 * us whether the device actually supports carrier detection in the first
+	 * place. We assume any device that does implements one of these two APIs.
+	 */
+	return supports_ethtool_carrier_detect (name) || supports_mii_carrier_detect (name);
+}
+
+static gboolean
+link_supports_vlans (NMPlatform *platform, int ifindex)
+{
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
+	const char *name = nm_platform_link_get_name (ifindex);
+	auto_g_free struct ethtool_gfeatures *features = NULL;
+	int idx, block, bit, size;
+
+	/* Only ARPHRD_ETHER links can possibly support VLANs. */
+	if (!rtnllink || rtnl_link_get_arptype (rtnllink) != ARPHRD_ETHER)
+		return FALSE;
+
+	if (!name)
+		return FALSE;
+
+	idx = ethtool_get_stringset_index (name, ETH_SS_FEATURES, "vlan-challenged");
+	if (idx == -1) {
+		debug ("vlan-challenged ethtool feature does not exist?");
+		return FALSE;
+	}
+
+	block = idx /  32;
+	bit = idx % 32;
+	size = block + 1;
+
+	features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block));
+	features->cmd = ETHTOOL_GFEATURES;
+	features->size = size;
+
+	if (!ethtool_get (name, features))
+		return FALSE;
+
+	return !(features->features[block].active & (1 << bit));
+}
+
+static gboolean
+link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length)
+{
+	auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL);
+	auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, length);
+
+	rtnl_link_set_addr (change, nladdr);
+
+	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+		char *mac = nm_utils_hwaddr_ntoa_len (address, length);
+
+		debug ("link: change %d: address %s (%lu bytes)", ifindex, mac, (unsigned long) length);
+		g_free (mac);
+	}
+
+	return link_change (platform, ifindex, change);
+}
+
+static gconstpointer
+link_get_address (NMPlatform *platform, int ifindex, size_t *length)
+{
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
+	struct nl_addr *nladdr;
+
+	nladdr = rtnllink ? rtnl_link_get_addr (rtnllink) : NULL;
+
+	if (length)
+		*length = nladdr ? nl_addr_get_len (nladdr) : 0;
+
+	return nladdr ? nl_addr_get_binary_addr (nladdr) : NULL;
+}
+
+static gboolean
+link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu)
+{
+	auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL);
+
+	rtnl_link_set_mtu (change, mtu);
+	debug ("link: change %d: mtu %lu", ifindex, (unsigned long)mtu);
+
+	return link_change (platform, ifindex, change);
+}
+
+static guint32
+link_get_mtu (NMPlatform *platform, int ifindex)
+{
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
+
+	return rtnllink ? rtnl_link_get_mtu (rtnllink) : 0;
+}
+
+static char *
+link_get_physical_port_id (NMPlatform *platform, int ifindex)
+{
+	const char *ifname;
+	char *path, *id;
+
+	ifname = nm_platform_link_get_name (ifindex);
+	if (!ifname)
+		return NULL;
+
+	ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
+
+	path = g_strdup_printf ("/sys/class/net/%s/phys_port_id", ifname);
+	id = sysctl_get (platform, path);
+	g_free (path);
+
+	return id;
+}
+
+static int
+vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags)
+{
+	struct nl_object *object = build_rtnl_link (0, name, NM_LINK_TYPE_VLAN);
+	struct rtnl_link *rtnllink = (struct rtnl_link *) object;
+	unsigned int kernel_flags;
+
+	kernel_flags = 0;
+	if (vlan_flags & NM_VLAN_FLAG_REORDER_HEADERS)
+		kernel_flags |= VLAN_FLAG_REORDER_HDR;
+	if (vlan_flags & NM_VLAN_FLAG_GVRP)
+		kernel_flags |= VLAN_FLAG_GVRP;
+	if (vlan_flags & NM_VLAN_FLAG_LOOSE_BINDING)
+		kernel_flags |= VLAN_FLAG_LOOSE_BINDING;
+
+	rtnl_link_set_link (rtnllink, parent);
+	rtnl_link_vlan_set_id (rtnllink, vlan_id);
+	rtnl_link_vlan_set_flags (rtnllink, kernel_flags);
+
+	debug ("link: add vlan '%s', parent %d, vlan id %d, flags %X (native: %X)",
+	       name, parent, vlan_id, (unsigned int) vlan_flags, kernel_flags);
+
+	return add_object (platform, object);
+}
+
+static gboolean
+vlan_get_info (NMPlatform *platform, int ifindex, int *parent, int *vlan_id)
+{
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
+
+	if (parent)
+		*parent = rtnllink ? rtnl_link_get_link (rtnllink) : 0;
+	if (vlan_id)
+		*vlan_id = rtnllink ? rtnl_link_vlan_get_id (rtnllink) : 0;
+
+	return !!rtnllink;
+}
+
+static gboolean
+vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to)
+{
+	/* We have to use link_get() because a "blank" rtnl_link won't have the
+	 * right data structures to be able to call rtnl_link_vlan_set_ingress_map()
+	 * on it. (Likewise below in vlan_set_egress_map().)
+	 */
+	auto_nl_object struct rtnl_link *change = link_get (platform, ifindex);
+
+	if (!change)
+		return FALSE;
+	rtnl_link_vlan_set_ingress_map (change, from, to);
+
+	debug ("link: change %d: vlan ingress map %d -> %d", ifindex, from, to);
+
+	return link_change (platform, ifindex, change);
+}
+
+static gboolean
+vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to)
+{
+	auto_nl_object struct rtnl_link *change = link_get (platform, ifindex);
+
+	if (!change)
+		return FALSE;
+	rtnl_link_vlan_set_egress_map (change, from, to);
+
+	debug ("link: change %d: vlan egress map %d -> %d", ifindex, from, to);
+
+	return link_change (platform, ifindex, change);
+}
+
+static gboolean
+link_enslave (NMPlatform *platform, int master, int slave)
+{
+	auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (slave, NULL);
+
+	rtnl_link_set_master (change, master);
+	debug ("link: change %d: enslave to master %d", slave, master);
+
+	return link_change (platform, slave, change);
+}
+
+static gboolean
+link_release (NMPlatform *platform, int master, int slave)
+{
+	return link_enslave (platform, 0, slave);
+}
+
+static int
+link_get_master (NMPlatform *platform, int slave)
+{
+	auto_nl_object struct rtnl_link *rtnllink = link_get (platform, slave);
+
+	return rtnllink ? rtnl_link_get_master (rtnllink) : 0;
+}
+
+static char *
+link_option_path (int master, const char *category, const char *option)
+{
+	const char *name = nm_platform_link_get_name (master);
+   
+	if (!name || !category || !option)
+		return NULL;
+
+	return g_strdup_printf ("/sys/class/net/%s/%s/%s",
+	                        ASSERT_VALID_PATH_COMPONENT (name),
+	                        ASSERT_VALID_PATH_COMPONENT (category),
+	                        ASSERT_VALID_PATH_COMPONENT (option));
+}
+
+static gboolean
+link_set_option (int master, const char *category, const char *option, const char *value)
+{
+	auto_g_free char *path = link_option_path (master, category, option);
+
+	return path && nm_platform_sysctl_set (path, value);
+}
+
+static char *
+link_get_option (int master, const char *category, const char *option)
+{
+	auto_g_free char *path = link_option_path (master, category, option);
+
+	return path ? nm_platform_sysctl_get (path) : NULL;
+}
+
+static const char *
+master_category (NMPlatform *platform, int master)
+{
+	switch (link_get_type (platform, master)) {
+	case NM_LINK_TYPE_BRIDGE:
+		return "bridge";
+	case NM_LINK_TYPE_BOND:
+		return "bonding";
+	default:
+		g_return_val_if_reached (NULL);
+		return NULL;
+	}
+}
+
+static const char *
+slave_category (NMPlatform *platform, int slave)
+{
+	int master = link_get_master (platform, slave);
+
+	if (master <= 0) {
+		platform->error = NM_PLATFORM_ERROR_NOT_SLAVE;
+		return NULL;
+	}
+
+	switch (link_get_type (platform, master)) {
+	case NM_LINK_TYPE_BRIDGE:
+		return "brport";
+	default:
+		g_return_val_if_reached (NULL);
+		return NULL;
+	}
+}
+
+static gboolean
+master_set_option (NMPlatform *platform, int master, const char *option, const char *value)
+{
+	return link_set_option (master, master_category (platform, master), option, value);
+}
+
+static char *
+master_get_option (NMPlatform *platform, int master, const char *option)
+{
+	return link_get_option (master, master_category (platform, master), option);
+}
+
+static gboolean
+slave_set_option (NMPlatform *platform, int slave, const char *option, const char *value)
+{
+	return link_set_option (slave, slave_category (platform, slave), option, value);
+}
+
+static char *
+slave_get_option (NMPlatform *platform, int slave, const char *option)
+{
+	return link_get_option (slave, slave_category (platform, slave), option);
+}
+
+static gboolean
+infiniband_partition_add (NMPlatform *platform, int parent, int p_key)
+{
+	const char *parent_name;
+	char *path, *id;
+	gboolean success;
+
+	parent_name = nm_platform_link_get_name (parent);
+	g_return_val_if_fail (parent_name != NULL, FALSE);
+
+	path = g_strdup_printf ("/sys/class/net/%s/create_child", ASSERT_VALID_PATH_COMPONENT (parent_name));
+	id = g_strdup_printf ("0x%04x", p_key);
+	success = nm_platform_sysctl_set (path, id);
+	g_free (id);
+	g_free (path);
+
+	if (success) {
+		auto_g_free char *ifname = g_strdup_printf ("%s.%04x", parent_name, p_key);
+		auto_nl_object struct rtnl_link *rtnllink = _nm_rtnl_link_alloc (0, ifname);
+
+		success = refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL);
+	}
+
+	return success;
+}
+
+static gboolean
+veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props)
+{
+	const char *ifname;
+	auto_g_free struct ethtool_stats *stats = NULL;
+	int peer_ifindex_stat;
+
+	ifname = nm_platform_link_get_name (ifindex);
+	if (!ifname)
+		return FALSE;
+
+	peer_ifindex_stat = ethtool_get_stringset_index (ifname, ETH_SS_STATS, "peer_ifindex");
+	if (peer_ifindex_stat == -1) {
+		debug ("%s: peer_ifindex ethtool stat does not exist?", ifname);
+		return FALSE;
+	}
+
+	stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64));
+	stats->cmd = ETHTOOL_GSTATS;
+	stats->n_stats = peer_ifindex_stat + 1;
+	if (!ethtool_get (ifname, stats))
+		return FALSE;
+
+	props->peer = stats->data[peer_ifindex_stat];
+	return TRUE;
+}
+
+static gboolean
+tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props)
+{
+	const char *ifname;
+	char *path, *val;
+	gboolean success = TRUE;
+
+	g_return_val_if_fail (props, FALSE);
+
+	memset (props, 0, sizeof (*props));
+	props->owner = -1;
+	props->group = -1;
+
+	ifname = nm_platform_link_get_name (ifindex);
+	if (!ifname || !nm_utils_iface_valid_name (ifname))
+		return FALSE;
+	ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
+
+	path = g_strdup_printf ("/sys/class/net/%s/owner", ifname);
+	val = nm_platform_sysctl_get (path);
+	g_free (path);
+	if (val) {
+		props->owner = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
+		if (errno)
+			success = FALSE;
+		g_free (val);
+	} else
+		success = FALSE;
+
+	path = g_strdup_printf ("/sys/class/net/%s/group", ifname);
+	val = nm_platform_sysctl_get (path);
+	g_free (path);
+	if (val) {
+		props->group = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
+		if (errno)
+			success = FALSE;
+		g_free (val);
+	} else
+		success = FALSE;
+
+	path = g_strdup_printf ("/sys/class/net/%s/tun_flags", ifname);
+	val = nm_platform_sysctl_get (path);
+	g_free (path);
+	if (val) {
+		gint64 flags;
+
+		flags = nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0);
+		if (!errno) {
+#ifndef IFF_MULTI_QUEUE
+			const int IFF_MULTI_QUEUE = 0x0100;
+#endif
+			props->mode = ((flags & TUN_TYPE_MASK) == TUN_TUN_DEV) ? "tun" : "tap";
+			props->no_pi = !!(flags & IFF_NO_PI);
+			props->vnet_hdr = !!(flags & IFF_VNET_HDR);
+			props->multi_queue = !!(flags & IFF_MULTI_QUEUE);
+		} else
+			success = FALSE;
+		g_free (val);
+	} else
+		success = FALSE;
+
+	return success;
+}
+
+static const struct nla_policy macvlan_info_policy[IFLA_MACVLAN_MAX + 1] = {
+	[IFLA_MACVLAN_MODE]  = { .type = NLA_U32 },
+#ifdef MACVLAN_FLAG_NOPROMISC
+	[IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 },
+#endif
+};
+
+static int
+macvlan_info_data_parser (struct nlattr *info_data, gpointer parser_data)
+{
+	NMPlatformMacvlanProperties *props = parser_data;
+	struct nlattr *tb[IFLA_MACVLAN_MAX + 1];
+	int err;
+
+	err = nla_parse_nested (tb, IFLA_MACVLAN_MAX, info_data,
+	                        (struct nla_policy *) macvlan_info_policy);
+	if (err < 0)
+		return err;
+
+	switch (nla_get_u32 (tb[IFLA_MACVLAN_MODE])) {
+	case MACVLAN_MODE_PRIVATE:
+		props->mode = "private";
+		break;
+	case MACVLAN_MODE_VEPA:
+		props->mode = "vepa";
+		break;
+	case MACVLAN_MODE_BRIDGE:
+		props->mode = "bridge";
+		break;
+	case MACVLAN_MODE_PASSTHRU:
+		props->mode = "passthru";
+		break;
+	default:
+		return -NLE_PARSE_ERR;
+	}
+
+#ifdef MACVLAN_FLAG_NOPROMISC
+	props->no_promisc = !!(nla_get_u16 (tb[IFLA_MACVLAN_FLAGS]) & MACVLAN_FLAG_NOPROMISC);
+#else
+	props->no_promisc = FALSE;
+#endif
+
+	return 0;
+}
+
+static gboolean
+macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProperties *props)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	auto_nl_object struct rtnl_link *rtnllink = NULL;
+	int err;
+
+	rtnllink = link_get (platform, ifindex);
+	if (!rtnllink)
+		return FALSE;
+
+	props->parent_ifindex = rtnl_link_get_link (rtnllink);
+
+	err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex,
+	                                    macvlan_info_data_parser, props);
+	if (err != 0) {
+		warning ("(%s) could not read properties: %s",
+		         rtnl_link_get_name (rtnllink), nl_geterror (err));
+	}
+	return (err == 0);
+}
+
+/* The installed kernel headers might not have VXLAN stuff at all, or
+ * they might have the original properties, but not PORT, GROUP6, or LOCAL6.
+ * So until we depend on kernel >= 3.11, we just ignore the actual enum
+ * in if_link.h and define the values ourselves.
+ */
+#define IFLA_VXLAN_UNSPEC      0
+#define IFLA_VXLAN_ID          1
+#define IFLA_VXLAN_GROUP       2
+#define IFLA_VXLAN_LINK        3
+#define IFLA_VXLAN_LOCAL       4
+#define IFLA_VXLAN_TTL         5
+#define IFLA_VXLAN_TOS         6
+#define IFLA_VXLAN_LEARNING    7
+#define IFLA_VXLAN_AGEING      8
+#define IFLA_VXLAN_LIMIT       9
+#define IFLA_VXLAN_PORT_RANGE 10
+#define IFLA_VXLAN_PROXY      11
+#define IFLA_VXLAN_RSC        12
+#define IFLA_VXLAN_L2MISS     13
+#define IFLA_VXLAN_L3MISS     14
+#define IFLA_VXLAN_PORT       15
+#define IFLA_VXLAN_GROUP6     16
+#define IFLA_VXLAN_LOCAL6     17
+#undef IFLA_VXLAN_MAX
+#define IFLA_VXLAN_MAX IFLA_VXLAN_LOCAL6
+
+static const struct nla_policy vxlan_info_policy[IFLA_VXLAN_MAX + 1] = {
+	[IFLA_VXLAN_ID]         = { .type = NLA_U32 },
+	[IFLA_VXLAN_GROUP]      = { .type = NLA_U32 },
+	[IFLA_VXLAN_GROUP6]     = { .type = NLA_UNSPEC,
+	                            .minlen = sizeof (struct in6_addr) },
+	[IFLA_VXLAN_LINK]       = { .type = NLA_U32 },
+	[IFLA_VXLAN_LOCAL]      = { .type = NLA_U32 },
+	[IFLA_VXLAN_LOCAL6]     = { .type = NLA_UNSPEC,
+	                            .minlen = sizeof (struct in6_addr) },
+	[IFLA_VXLAN_TOS]        = { .type = NLA_U8 },
+	[IFLA_VXLAN_TTL]        = { .type = NLA_U8 },
+	[IFLA_VXLAN_LEARNING]   = { .type = NLA_U8 },
+	[IFLA_VXLAN_AGEING]     = { .type = NLA_U32 },
+	[IFLA_VXLAN_LIMIT]      = { .type = NLA_U32 },
+	[IFLA_VXLAN_PORT_RANGE] = { .type = NLA_UNSPEC,
+	                            .minlen  = sizeof (struct ifla_vxlan_port_range) },
+	[IFLA_VXLAN_PROXY]      = { .type = NLA_U8 },
+	[IFLA_VXLAN_RSC]        = { .type = NLA_U8 },
+	[IFLA_VXLAN_L2MISS]     = { .type = NLA_U8 },
+	[IFLA_VXLAN_L3MISS]     = { .type = NLA_U8 },
+	[IFLA_VXLAN_PORT]       = { .type = NLA_U16 },
+};
+
+static int
+vxlan_info_data_parser (struct nlattr *info_data, gpointer parser_data)
+{
+	NMPlatformVxlanProperties *props = parser_data;
+	struct nlattr *tb[IFLA_VXLAN_MAX + 1];
+	struct ifla_vxlan_port_range *range;
+	int err;
+
+	err = nla_parse_nested (tb, IFLA_VXLAN_MAX, info_data,
+	                        (struct nla_policy *) vxlan_info_policy);
+	if (err < 0)
+		return err;
+
+	memset (props, 0, sizeof (*props));
+
+	props->parent_ifindex = tb[IFLA_VXLAN_LINK] ? nla_get_u32 (tb[IFLA_VXLAN_LINK]) : 0;
+	props->id = nla_get_u32 (tb[IFLA_VXLAN_ID]);
+	if (tb[IFLA_VXLAN_GROUP])
+		props->group = nla_get_u32 (tb[IFLA_VXLAN_GROUP]);
+	if (tb[IFLA_VXLAN_LOCAL])
+		props->local = nla_get_u32 (tb[IFLA_VXLAN_LOCAL]);
+	if (tb[IFLA_VXLAN_GROUP6])
+		memcpy (&props->group6, nla_data (tb[IFLA_VXLAN_GROUP6]), sizeof (props->group6));
+	if (tb[IFLA_VXLAN_LOCAL6])
+		memcpy (&props->local6, nla_data (tb[IFLA_VXLAN_LOCAL6]), sizeof (props->local6));
+
+	props->ageing = nla_get_u32 (tb[IFLA_VXLAN_AGEING]);
+	props->limit = nla_get_u32 (tb[IFLA_VXLAN_LIMIT]);
+	props->tos = nla_get_u8 (tb[IFLA_VXLAN_TOS]);
+	props->ttl = nla_get_u8 (tb[IFLA_VXLAN_TTL]);
+
+	props->dst_port = nla_get_u16 (tb[IFLA_VXLAN_PORT]);
+	range = nla_data (tb[IFLA_VXLAN_PORT_RANGE]);
+	props->src_port_min = range->low;
+	props->src_port_max = range->high;
+
+	props->learning = !!nla_get_u8 (tb[IFLA_VXLAN_LEARNING]);
+	props->proxy = !!nla_get_u8 (tb[IFLA_VXLAN_PROXY]);
+	props->rsc = !!nla_get_u8 (tb[IFLA_VXLAN_RSC]);
+	props->l2miss = !!nla_get_u8 (tb[IFLA_VXLAN_L2MISS]);
+	props->l3miss = !!nla_get_u8 (tb[IFLA_VXLAN_L3MISS]);
+
+	return 0;
+}
+
+static gboolean
+vxlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformVxlanProperties *props)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	int err;
+
+	err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex,
+	                                    vxlan_info_data_parser, props);
+	if (err != 0) {
+		warning ("(%s) could not read properties: %s",
+		         link_get_name (platform, ifindex), nl_geterror (err));
+	}
+	return (err == 0);
+}
+
+static const struct nla_policy gre_info_policy[IFLA_GRE_MAX + 1] = {
+	[IFLA_GRE_LINK]     = { .type = NLA_U32 },
+	[IFLA_GRE_IFLAGS]   = { .type = NLA_U16 },
+	[IFLA_GRE_OFLAGS]   = { .type = NLA_U16 },
+	[IFLA_GRE_IKEY]     = { .type = NLA_U32 },
+	[IFLA_GRE_OKEY]     = { .type = NLA_U32 },
+	[IFLA_GRE_LOCAL]    = { .type = NLA_U32 },
+	[IFLA_GRE_REMOTE]   = { .type = NLA_U32 },
+	[IFLA_GRE_TTL]      = { .type = NLA_U8 },
+	[IFLA_GRE_TOS]      = { .type = NLA_U8 },
+	[IFLA_GRE_PMTUDISC] = { .type = NLA_U8 },
+};
+
+static int
+gre_info_data_parser (struct nlattr *info_data, gpointer parser_data)
+{
+	NMPlatformGreProperties *props = parser_data;
+	struct nlattr *tb[IFLA_GRE_MAX + 1];
+	int err;
+
+	err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data,
+	                        (struct nla_policy *) gre_info_policy);
+	if (err < 0)
+		return err;
+
+	props->parent_ifindex = tb[IFLA_GRE_LINK] ? nla_get_u32 (tb[IFLA_GRE_LINK]) : 0;
+	props->input_flags = nla_get_u16 (tb[IFLA_GRE_IFLAGS]);
+	props->output_flags = nla_get_u16 (tb[IFLA_GRE_OFLAGS]);
+	props->input_key = (props->input_flags & GRE_KEY) ? nla_get_u32 (tb[IFLA_GRE_IKEY]) : 0;
+	props->output_key = (props->output_flags & GRE_KEY) ? nla_get_u32 (tb[IFLA_GRE_OKEY]) : 0;
+	props->local = nla_get_u32 (tb[IFLA_GRE_LOCAL]);
+	props->remote = nla_get_u32 (tb[IFLA_GRE_REMOTE]);
+	props->tos = nla_get_u8 (tb[IFLA_GRE_TOS]);
+	props->ttl = nla_get_u8 (tb[IFLA_GRE_TTL]);
+	props->path_mtu_discovery = !!nla_get_u8 (tb[IFLA_GRE_PMTUDISC]);
+
+	return 0;
+}
+
+static gboolean
+gre_get_properties (NMPlatform *platform, int ifindex, NMPlatformGreProperties *props)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	int err;
+
+	err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex,
+	                                    gre_info_data_parser, props);
+	if (err != 0) {
+		warning ("(%s) could not read properties: %s",
+		         link_get_name (platform, ifindex), nl_geterror (err));
+	}
+	return (err == 0);
+}
+
+static WifiData *
+wifi_get_wifi_data (NMPlatform *platform, int ifindex)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	WifiData *wifi_data;
+
+	wifi_data = g_hash_table_lookup (priv->wifi_data, GINT_TO_POINTER (ifindex));
+	if (!wifi_data) {
+		NMLinkType type;
+		const char *ifname;
+
+		type = link_get_type (platform, ifindex);
+		ifname = link_get_name (platform, ifindex);
+
+		if (type == NM_LINK_TYPE_WIFI)
+			wifi_data = wifi_utils_init (ifname, ifindex, TRUE);
+		else if (type == NM_LINK_TYPE_OLPC_MESH) {
+			/* The kernel driver now uses nl80211, but we force use of WEXT because
+			 * the cfg80211 interactions are not quite ready to support access to
+			 * mesh control through nl80211 just yet.
+			 */
+#if HAVE_WEXT
+			wifi_data = wifi_wext_init (ifname, ifindex, FALSE);
+#endif
+		}
+
+		if (wifi_data)
+			g_hash_table_insert (priv->wifi_data, GINT_TO_POINTER (ifindex), wifi_data);
+	}
+
+	return wifi_data;
+}
+
+static gboolean
+wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return FALSE;
+
+	if (caps)
+		*caps = wifi_utils_get_caps (wifi_data);
+	return TRUE;
+}
+
+static gboolean
+wifi_get_bssid (NMPlatform *platform, int ifindex, struct ether_addr *bssid)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return FALSE;
+	return wifi_utils_get_bssid (wifi_data, bssid);
+}
+
+static GByteArray *
+wifi_get_ssid (NMPlatform *platform, int ifindex)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return NULL;
+	return wifi_utils_get_ssid (wifi_data);
+}
+
+static guint32
+wifi_get_frequency (NMPlatform *platform, int ifindex)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return 0;
+	return wifi_utils_get_freq (wifi_data);
+}
+
+static gboolean
+wifi_get_quality (NMPlatform *platform, int ifindex)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return FALSE;
+	return wifi_utils_get_qual (wifi_data);
+}
+
+static guint32
+wifi_get_rate (NMPlatform *platform, int ifindex)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return FALSE;
+	return wifi_utils_get_rate (wifi_data);
+}
+
+static NM80211Mode
+wifi_get_mode (NMPlatform *platform, int ifindex)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return NM_802_11_MODE_UNKNOWN;
+
+	return wifi_utils_get_mode (wifi_data);
+}
+
+static void
+wifi_set_mode (NMPlatform *platform, int ifindex, NM80211Mode mode)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (wifi_data)
+		wifi_utils_set_mode (wifi_data, mode);
+}
+
+static guint32
+wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return 0;
+
+	return wifi_utils_find_freq (wifi_data, freqs);
+}
+
+static void
+wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean running)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (wifi_data)
+		wifi_utils_indicate_addressing_running (wifi_data, running);
+}
+
+
+static guint32
+mesh_get_channel (NMPlatform *platform, int ifindex)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return 0;
+
+	return wifi_utils_get_mesh_channel (wifi_data);
+}
+
+static gboolean
+mesh_set_channel (NMPlatform *platform, int ifindex, guint32 channel)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return FALSE;
+
+	return wifi_utils_set_mesh_channel (wifi_data, channel);
+}
+
+static gboolean
+mesh_set_ssid (NMPlatform *platform, int ifindex, const GByteArray *ssid)
+{
+	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+	if (!wifi_data)
+		return FALSE;
+
+	return wifi_utils_set_mesh_ssid (wifi_data, ssid);
+}
+
+static gboolean
+link_get_wake_on_lan (NMPlatform *platform, int ifindex)
+{
+	NMLinkType type = link_get_type (platform, ifindex);
+
+	if (type == NM_LINK_TYPE_ETHERNET) {
+		struct ethtool_wolinfo wol;
+
+		memset (&wol, 0, sizeof (wol));
+		wol.cmd = ETHTOOL_GWOL;
+		if (!ethtool_get (link_get_name (platform, ifindex), &wol))
+			return FALSE;
+
+		return wol.wolopts != 0;
+	} else if (type == NM_LINK_TYPE_WIFI) {
+		WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
+
+		if (!wifi_data)
+			return FALSE;
+
+		return wifi_utils_get_wowlan (wifi_data);
+	} else
+		return FALSE;
+}
+
+/******************************************************************/
+
+static gboolean
+_address_match (struct rtnl_addr *addr, int family, int ifindex)
+{
+	g_return_val_if_fail (addr, FALSE);
+
+	return rtnl_addr_get_family (addr) == family &&
+	       rtnl_addr_get_ifindex (addr) == ifindex;
+}
+
+static GArray *
+ip4_address_get_all (NMPlatform *platform, int ifindex)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	GArray *addresses;
+	NMPlatformIP4Address address;
+	struct nl_object *object;
+
+	addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address));
+
+	for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
+		if (_address_match ((struct rtnl_addr *) object, AF_INET, ifindex)) {
+			if (init_ip4_address (&address, (struct rtnl_addr *) object))
+				g_array_append_val (addresses, address);
+		}
+	}
+
+	return addresses;
+}
+
+static GArray *
+ip6_address_get_all (NMPlatform *platform, int ifindex)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	GArray *addresses;
+	NMPlatformIP6Address address;
+	struct nl_object *object;
+
+	addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Address));
+
+	for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
+		if (_address_match ((struct rtnl_addr *) object, AF_INET6, ifindex)) {
+			if (init_ip6_address (&address, (struct rtnl_addr *) object))
+				g_array_append_val (addresses, address);
+		}
+	}
+
+	return addresses;
+}
+
+#define IPV4LL_NETWORK (htonl (0xA9FE0000L))
+#define IPV4LL_NETMASK (htonl (0xFFFF0000L))
+
+static gboolean
+ip4_is_link_local (const struct in_addr *src)
+{
+	return (src->s_addr & IPV4LL_NETMASK) == IPV4LL_NETWORK;
+}
+
+static struct nl_object *
+build_rtnl_addr (int family,
+                 int ifindex,
+                 gconstpointer addr,
+                 gconstpointer peer_addr,
+                 int plen,
+                 guint32 lifetime,
+                 guint32 preferred,
+                 guint flags,
+                 const char *label)
+{
+	auto_nl_addr struct rtnl_addr *rtnladdr = _nm_rtnl_addr_alloc (ifindex);
+	struct rtnl_addr *rtnladdr_copy;
+	int addrlen = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr);
+	auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (family, addr, addrlen);
+	int nle;
+
+	/* IP address */
+	nle = rtnl_addr_set_local (rtnladdr, nladdr);
+	if (nle) {
+		error ("build_rtnl_addr(): rtnl_addr_set_local failed with %s (%d)", nl_geterror (nle), nle);
+		return NULL;
+	}
+
+	/* Tighten scope (IPv4 only) */
+	if (family == AF_INET && ip4_is_link_local (addr))
+		rtnl_addr_set_scope (rtnladdr, rtnl_str2scope ("link"));
+
+	/* IPv4 Broadcast address */
+	if (family == AF_INET) {
+		in_addr_t bcast;
+		auto_nl_addr struct nl_addr *bcaddr = NULL;
+
+		bcast = *((in_addr_t *) addr) | ~nm_utils_ip4_prefix_to_netmask (plen);
+		bcaddr = _nm_nl_addr_build (family, &bcast, addrlen);
+		g_assert (bcaddr);
+		rtnl_addr_set_broadcast (rtnladdr, bcaddr);
+	}
+
+	/* Peer/point-to-point address */
+	if (peer_addr) {
+		auto_nl_addr struct nl_addr *nlpeer = _nm_nl_addr_build (family, peer_addr, addrlen);
+
+		nle = rtnl_addr_set_peer (rtnladdr, nlpeer);
+		if (nle && nle != -NLE_AF_NOSUPPORT) {
+			/* IPv6 doesn't support peer addresses yet */
+			error ("build_rtnl_addr(): rtnl_addr_set_peer failed with %s (%d)", nl_geterror (nle), nle);
+			return NULL;
+		}
+	}
+
+	rtnl_addr_set_prefixlen (rtnladdr, plen);
+	if (lifetime) {
+		/* note that here we set the relative timestamps (ticking from *now*).
+		 * Contrary to the rtnl_addr objects from our cache, which have absolute
+		 * timestamps (see _rtnl_addr_hack_lifetimes_rel_to_abs()).
+		 *
+		 * This is correct, because we only use build_rtnl_addr() for
+		 * add_object(), delete_object() and cache search (ip_address_exists). */
+		rtnl_addr_set_valid_lifetime (rtnladdr, lifetime);
+		rtnl_addr_set_preferred_lifetime (rtnladdr, preferred);
+	}
+	if (flags) {
+		if ((flags & ~0xFF) && !check_support_kernel_extended_ifa_flags (nm_platform_get ())) {
+			/* Older kernels don't accept unknown netlink attributes.
+			 *
+			 * With commit libnl commit 5206c050504f8676a24854519b9c351470fb7cc6, libnl will only set
+			 * the extended address flags attribute IFA_FLAGS when necessary (> 8 bit). But it's up to
+			 * us not to shove those extended flags on to older kernels.
+			 *
+			 * Just silently clear them. The kernel should ignore those unknown flags anyway. */
+			flags &= 0xFF;
+		}
+		rtnl_addr_set_flags (rtnladdr, flags);
+	}
+	if (label && *label)
+		rtnl_addr_set_label (rtnladdr, label);
+
+	rtnladdr_copy = rtnladdr;
+	rtnladdr = NULL;
+	return (struct nl_object *) rtnladdr_copy;
+}
+
+static gboolean
+ip4_address_add (NMPlatform *platform,
+                 int ifindex,
+                 in_addr_t addr,
+                 in_addr_t peer_addr,
+                 int plen,
+                 guint32 lifetime,
+                 guint32 preferred,
+                 const char *label)
+{
+	return add_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr,
+	                                              peer_addr ? &peer_addr : NULL,
+	                                              plen, lifetime, preferred, 0,
+	                                              label));
+}
+
+static gboolean
+ip6_address_add (NMPlatform *platform,
+                 int ifindex,
+                 struct in6_addr addr,
+                 struct in6_addr peer_addr,
+                 int plen,
+                 guint32 lifetime,
+                 guint32 preferred,
+                 guint flags)
+{
+	return add_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr,
+	                                              IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr,
+	                                              plen, lifetime, preferred, flags,
+	                                              NULL));
+}
+
+static gboolean
+ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen)
+{
+	return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, NULL, plen, 0, 0, 0, NULL), TRUE);
+}
+
+static gboolean
+ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
+{
+	return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0, NULL), TRUE);
+}
+
+static gboolean
+ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer addr, int plen)
+{
+	auto_nl_object struct nl_object *object = build_rtnl_addr (family, ifindex, addr, NULL, plen, 0, 0, 0, NULL);
+	auto_nl_object struct nl_object *cached_object = nl_cache_search (choose_cache (platform, object), object);
+
+	return !!cached_object;
+}
+
+static gboolean
+ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen)
+{
+	return ip_address_exists (platform, AF_INET, ifindex, &addr, plen);
+}
+
+static gboolean
+ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
+{
+	return ip_address_exists (platform, AF_INET6, ifindex, &addr, plen);
+}
+
+/******************************************************************/
+
+static gboolean
+_route_match (struct rtnl_route *rtnlroute, int family, int ifindex)
+{
+	struct rtnl_nexthop *nexthop;
+
+	g_return_val_if_fail (rtnlroute, FALSE);
+
+	if (rtnl_route_get_type (rtnlroute) != RTN_UNICAST ||
+	    rtnl_route_get_table (rtnlroute) != RT_TABLE_MAIN ||
+	    rtnl_route_get_protocol (rtnlroute) == RTPROT_KERNEL ||
+	    rtnl_route_get_family (rtnlroute) != family ||
+	    rtnl_route_get_nnexthops (rtnlroute) != 1)
+		return FALSE;
+
+	nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
+	return rtnl_route_nh_get_ifindex (nexthop) == ifindex;
+}
+
+static GArray *
+ip4_route_get_all (NMPlatform *platform, int ifindex, gboolean include_default)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	GArray *routes;
+	NMPlatformIP4Route route;
+	struct nl_object *object;
+
+	routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
+
+	for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
+		if (_route_match ((struct rtnl_route *) object, AF_INET, ifindex)) {
+			if (init_ip4_route (&route, (struct rtnl_route *) object)) {
+				if (route.plen != 0 || include_default)
+					g_array_append_val (routes, route);
+			}
+		}
+	}
+
+	return routes;
+}
+
+static GArray *
+ip6_route_get_all (NMPlatform *platform, int ifindex, gboolean include_default)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	GArray *routes;
+	NMPlatformIP6Route route;
+	struct nl_object *object;
+
+	routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route));
+
+	for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
+		if (_route_match ((struct rtnl_route *) object, AF_INET6, ifindex)) {
+			if (init_ip6_route (&route, (struct rtnl_route *) object)) {
+				if (route.plen != 0 || include_default)
+					g_array_append_val (routes, route);
+			}
+		}
+	}
+
+	return routes;
+}
+
+static void
+clear_host_address (int family, const void *network, int plen, void *dst)
+{
+	g_return_if_fail (plen == (guint8)plen);
+	g_return_if_fail (network);
+
+	switch (family) {
+	case AF_INET:
+		*((in_addr_t *) dst) = nm_utils_ip4_address_clear_host_address (*((in_addr_t *) network), plen);
+		break;
+	case AF_INET6:
+		nm_utils_ip6_address_clear_host_address ((struct in6_addr *) dst, (const struct in6_addr *) network, plen);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+static struct nl_object *
+build_rtnl_route (int family, int ifindex, NMPlatformSource source,
+                  gconstpointer network, int plen, gconstpointer gateway,
+                  int metric, int mss)
+{
+	guint32 network_clean[4];
+	struct rtnl_route *rtnlroute;
+	struct rtnl_nexthop *nexthop;
+	int addrlen = (family == AF_INET) ? sizeof (in_addr_t) : sizeof (struct in6_addr);
+	/* Workaround a libnl bug by using zero destination address length for default routes */
+	auto_nl_addr struct nl_addr *dst = NULL;
+	auto_nl_addr struct nl_addr *gw = gateway ? _nm_nl_addr_build (family, gateway, addrlen) : NULL;
+
+	/* There seem to be problems adding a route with non-zero host identifier.
+	 * Adding IPv6 routes is simply ignored, without error message.
+	 * In the IPv4 case, we got an error. Thus, we have to make sure, that
+	 * the address is sane. */
+	clear_host_address (family, network, plen, network_clean);
+	dst = _nm_nl_addr_build (family, network_clean, plen ? addrlen : 0);
+	nl_addr_set_prefixlen (dst, plen);
+
+	rtnlroute = _nm_rtnl_route_alloc ();
+	rtnl_route_set_table (rtnlroute, RT_TABLE_MAIN);
+	rtnl_route_set_tos (rtnlroute, 0);
+	rtnl_route_set_dst (rtnlroute, dst);
+	rtnl_route_set_priority (rtnlroute, metric);
+	rtnl_route_set_family (rtnlroute, family);
+	rtnl_route_set_protocol (rtnlroute, source_to_rtprot (source));
+
+	nexthop = _nm_rtnl_route_nh_alloc ();
+	rtnl_route_nh_set_ifindex (nexthop, ifindex);
+	if (gw && !nl_addr_iszero (gw))
+		rtnl_route_nh_set_gateway (nexthop, gw);
+	rtnl_route_add_nexthop (rtnlroute, nexthop);
+
+	if (mss > 0)
+		rtnl_route_set_metric (rtnlroute, RTAX_ADVMSS, mss);
+
+	return (struct nl_object *) rtnlroute;
+}
+
+static gboolean
+ip4_route_add (NMPlatform *platform, int ifindex, NMPlatformSource source,
+               in_addr_t network, int plen, in_addr_t gateway,
+               int metric, int mss)
+{
+	return add_object (platform, build_rtnl_route (AF_INET, ifindex, source, &network, plen, &gateway, metric, mss));
+}
+
+static gboolean
+ip6_route_add (NMPlatform *platform, int ifindex, NMPlatformSource source,
+               struct in6_addr network, int plen, struct in6_addr gateway,
+               int metric, int mss)
+{
+	return add_object (platform, build_rtnl_route (AF_INET6, ifindex, source, &network, plen, &gateway, metric, mss));
+}
+
+static struct rtnl_route *
+route_search_cache (struct nl_cache *cache, int family, int ifindex, const void *network, int plen, int metric)
+{
+	guint32 network_clean[4], dst_clean[4];
+	struct nl_object *object;
+
+	clear_host_address (family, network, plen, network_clean);
+
+	for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) {
+		struct nl_addr *dst;
+		struct rtnl_route *rtnlroute = (struct rtnl_route *) object;
+
+		if (!_route_match (rtnlroute, family, ifindex))
+			continue;
+
+		if (metric && metric != rtnl_route_get_priority (rtnlroute))
+			continue;
+
+		dst = rtnl_route_get_dst (rtnlroute);
+		if (   !dst
+		    || nl_addr_get_family (dst) != family
+		    || nl_addr_get_prefixlen (dst) != plen)
+			continue;
+
+		clear_host_address (family, nl_addr_get_binary_addr (dst), plen, dst_clean);
+		if (memcmp (dst_clean, network_clean,
+		            family == AF_INET ? sizeof (guint32) : sizeof (struct in6_addr)) != 0)
+			continue;
+
+		rtnl_route_get (rtnlroute);
+		return rtnlroute;
+	}
+	return NULL;
+}
+
+static gboolean
+refresh_route (NMPlatform *platform, int family, int ifindex, const void *network, int plen, int metric)
+{
+	struct nl_cache *cache;
+	auto_nl_object struct rtnl_route *cached_object = NULL;
+
+	cache = choose_cache_by_type (platform, family == AF_INET ? OBJECT_TYPE_IP4_ROUTE : OBJECT_TYPE_IP6_ROUTE);
+	cached_object = route_search_cache (cache, family, ifindex, network, plen, metric);
+
+	if (cached_object)
+		return refresh_object (platform, (struct nl_object *) cached_object, TRUE, NM_PLATFORM_REASON_INTERNAL);
+	return TRUE;
+}
+
+static gboolean
+ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, int metric)
+{
+	in_addr_t gateway = 0;
+	struct rtnl_route *cached_object;
+	struct nl_object *route = build_rtnl_route (AF_INET, ifindex, NM_PLATFORM_SOURCE_UNKNOWN, &network, plen, &gateway, metric, 0);
+	uint8_t scope = RT_SCOPE_NOWHERE;
+	struct nl_cache *cache;
+
+	g_return_val_if_fail (route, FALSE);
+
+	cache = choose_cache_by_type (platform, OBJECT_TYPE_IP4_ROUTE);
+
+	/* when deleting an IPv4 route, several fields of the provided route must match.
+	 * Lookup in the cache so that we hopefully get the right values. */
+	cached_object = (struct rtnl_route *) nl_cache_search (cache, route);
+	if (!cached_object)
+		cached_object = route_search_cache (cache, AF_INET, ifindex, &network, plen, metric);
+
+	if (!_nl_has_capability (1 /* NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE */)) {
+		/* When searching for a matching IPv4 route to delete, the kernel
+		 * searches for a matching scope, unless the RTM_DELROUTE message
+		 * specifies RT_SCOPE_NOWHERE (see fib_table_delete()).
+		 *
+		 * However, if we set the scope of @rtnlroute to RT_SCOPE_NOWHERE (or
+		 * leave it unset), rtnl_route_build_msg() will reset the scope to
+		 * rtnl_route_guess_scope() -- which probably guesses wrong.
+		 *
+		 * As a workaround, we look at the cached route and use that scope.
+		 *
+		 * Newer versions of libnl, no longer reset the scope if explicitly set to RT_SCOPE_NOWHERE.
+		 * So, this workaround is only needed unless we have NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE.
+		 **/
+
+		if (cached_object)
+			scope = rtnl_route_get_scope (cached_object);
+
+		if (scope == RT_SCOPE_NOWHERE) {
+			/* If we would set the scope to RT_SCOPE_NOWHERE, libnl would guess the scope.
+			 * But probably it will guess 'link' because we set the next hop of the route
+			 * to zero (0.0.0.0). A better guess is 'global'. */
+			scope = RT_SCOPE_UNIVERSE;
+		}
+	}
+	rtnl_route_set_scope ((struct rtnl_route *) route, scope);
+
+	if (cached_object)
+		rtnl_route_set_tos ((struct rtnl_route *) route, rtnl_route_get_tos (cached_object));
+
+	/* The following fields are also relevant when comparing the route, but the default values
+	 * are already as we want them:
+	 *
+	 * type: RTN_UNICAST (setting to zero would ignore the type, but we only want to delete RTN_UNICAST)
+	 * pref_src: NULL
+	 */
+
+	rtnl_route_put (cached_object);
+	return delete_object (platform, route, FALSE) && refresh_route (platform, AF_INET, ifindex, &network, plen, metric);
+}
+
+static gboolean
+ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, int metric)
+{
+	struct in6_addr gateway = IN6ADDR_ANY_INIT;
+
+	return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, NM_PLATFORM_SOURCE_UNKNOWN ,&network, plen, &gateway, metric, 0), FALSE) &&
+	    refresh_route (platform, AF_INET6, ifindex, &network, plen, metric);
+}
+
+static gboolean
+ip_route_exists (NMPlatform *platform, int family, int ifindex, gpointer network, int plen, int metric)
+{
+	auto_nl_object struct nl_object *object = build_rtnl_route (family, ifindex,
+	                                                            NM_PLATFORM_SOURCE_UNKNOWN,
+	                                                            network, plen, NULL, metric, 0);
+	struct nl_cache *cache = choose_cache (platform, object);
+	auto_nl_object struct nl_object *cached_object = nl_cache_search (cache, object);
+
+	if (!cached_object)
+		cached_object = (struct nl_object *) route_search_cache (cache, family, ifindex, network, plen, metric);
+	return !!cached_object;
+}
+
+static gboolean
+ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, int metric)
+{
+	return ip_route_exists (platform, AF_INET, ifindex, &network, plen, metric);
+}
+
+static gboolean
+ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, int metric)
+{
+	return ip_route_exists (platform, AF_INET6, ifindex, &network, plen, metric);
+}
+
+/******************************************************************/
+
+#define EVENT_CONDITIONS      ((GIOCondition) (G_IO_IN | G_IO_PRI))
+#define ERROR_CONDITIONS      ((GIOCondition) (G_IO_ERR | G_IO_NVAL))
+#define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP))
+
+static int
+verify_source (struct nl_msg *msg, gpointer user_data)
+{
+	struct ucred *creds = nlmsg_get_creds (msg);
+
+	if (!creds || creds->pid || creds->uid || creds->gid) {
+		if (creds)
+			warning ("netlink: received non-kernel message (pid %d uid %d gid %d)",
+					creds->pid, creds->uid, creds->gid);
+		else
+			warning ("netlink: received message without credentials");
+		return NL_STOP;
+	}
+
+	return NL_OK;
+}
+
+static gboolean
+event_handler (GIOChannel *channel,
+               GIOCondition io_condition,
+               gpointer user_data)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data);
+	int nle;
+
+	nle = nl_recvmsgs_default (priv->nlh_event);
+	if (nle < 0)
+		switch (nle) {
+		case -NLE_DUMP_INTR:
+			/* this most likely happens due to our request (RTM_GETADDR, AF_INET6, NLM_F_DUMP)
+			 * to detect support for support_kernel_extended_ifa_flags. This is not critical
+			 * and can happen easily. */
+			debug ("Uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
+			break;
+		default:
+			error ("Failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
+			break;
+	}
+	return TRUE;
+}
+
+static struct nl_sock *
+setup_socket (gboolean event, gpointer user_data)
+{
+	struct nl_sock *sock;
+	int nle;
+
+	sock = nl_socket_alloc ();
+	g_return_val_if_fail (sock, NULL);
+
+	/* Only ever accept messages from kernel */
+	nle = nl_socket_modify_cb (sock, NL_CB_MSG_IN, NL_CB_CUSTOM, verify_source, user_data);
+	g_assert (!nle);
+
+	/* Dispatch event messages (event socket only) */
+	if (event) {
+		nl_socket_modify_cb (sock, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data);
+		nl_socket_disable_seq_check (sock);
+	}
+
+	nle = nl_connect (sock, NETLINK_ROUTE);
+	g_assert (!nle);
+	nle = nl_socket_set_passcred (sock, 1);
+	g_assert (!nle);
+
+	return sock;
+}
+
+/******************************************************************/
+
+static void
+udev_device_added (NMPlatform *platform,
+                   GUdevDevice *udev_device)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	auto_nl_object struct rtnl_link *rtnllink = NULL;
+	const char *ifname;
+	int ifindex;
+	gboolean was_announceable = FALSE;
+
+	ifname = g_udev_device_get_name (udev_device);
+	if (!ifname) {
+		debug ("udev-add: failed to get device's interface");
+		return;
+	}
+
+	if (g_udev_device_get_property (udev_device, "IFINDEX"))
+		ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
+	else {
+		warning ("(%s): udev-add: failed to get device's ifindex", ifname);
+		return;
+	}
+	if (ifindex <= 0) {
+		warning ("(%s): udev-add: retrieved invalid IFINDEX=%d", ifname, ifindex);
+		return;
+	}
+
+	if (!g_udev_device_get_sysfs_path (udev_device)) {
+		debug ("(%s): udev-add: couldn't determine device path; ignoring...", ifname);
+		return;
+	}
+
+	rtnllink = rtnl_link_get (priv->link_cache, ifindex);
+	if (rtnllink)
+		was_announceable = link_is_announceable (platform, rtnllink);
+
+	g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex),
+	                     g_object_ref (udev_device));
+
+	/* Announce devices only if they also have been discovered via Netlink. */
+	if (rtnllink && link_is_announceable (platform, rtnllink))
+		announce_object (platform, (struct nl_object *) rtnllink, was_announceable ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL);
+}
+
+static void
+udev_device_removed (NMPlatform *platform,
+                     GUdevDevice *udev_device)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	auto_nl_object struct rtnl_link *rtnllink = NULL;
+	int ifindex = 0;
+	gboolean was_announceable = FALSE;
+
+	if (g_udev_device_get_property (udev_device, "IFINDEX"))
+		ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
+	else {
+		GHashTableIter iter;
+		gpointer key, value;
+
+		/* This should not happen, but just to be sure.
+		 * If we can't get IFINDEX, go through the devices and
+		 * compare the pointers.
+		 */
+		g_hash_table_iter_init (&iter, priv->udev_devices);
+		while (g_hash_table_iter_next (&iter, &key, &value)) {
+			if ((GUdevDevice *)value == udev_device) {
+				ifindex = GPOINTER_TO_INT (key);
+				break;
+			}
+		}
+	}
+
+	debug ("udev-remove: IFINDEX=%d", ifindex);
+	if (ifindex <= 0)
+		return;
+
+	rtnllink = rtnl_link_get (priv->link_cache, ifindex);
+	if (rtnllink)
+		was_announceable = link_is_announceable (platform, rtnllink);
+
+	g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex));
+
+	/* Announce device removal if it is no longer announceable. */
+	if (was_announceable && !link_is_announceable (platform, rtnllink))
+		announce_object (platform, (struct nl_object *) rtnllink, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL);
+}
+
+static void
+handle_udev_event (GUdevClient *client,
+                   const char *action,
+                   GUdevDevice *udev_device,
+                   gpointer user_data)
+{
+	NMPlatform *platform = NM_PLATFORM (user_data);
+	const char *subsys;
+	const char *ifindex;
+	guint64 seqnum;
+
+	g_return_if_fail (action != NULL);
+
+	/* A bit paranoid */
+	subsys = g_udev_device_get_subsystem (udev_device);
+	g_return_if_fail (!g_strcmp0 (subsys, "net"));
+
+	ifindex = g_udev_device_get_property (udev_device, "IFINDEX");
+	seqnum = g_udev_device_get_seqnum (udev_device);
+	debug ("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT,
+	       action, subsys, g_udev_device_get_name (udev_device),
+	       ifindex ? ifindex : "unknown", seqnum);
+
+	if (!strcmp (action, "add") || !strcmp (action, "move"))
+		udev_device_added (platform, udev_device);
+	if (!strcmp (action, "remove"))
+		udev_device_removed (platform, udev_device);
+}
+
+/******************************************************************/
+
+static void
+nm_linux_platform_init (NMLinuxPlatform *platform)
+{
+}
+
+static gboolean
+setup (NMPlatform *platform)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+	const char *udev_subsys[] = { "net", NULL };
+	GUdevEnumerator *enumerator;
+	GList *devices, *iter;
+	int channel_flags;
+	gboolean status;
+	int nle;
+	struct nl_object *object;
+
+	/* Initialize netlink socket for requests */
+	priv->nlh = setup_socket (FALSE, platform);
+	g_assert (priv->nlh);
+	debug ("Netlink socket for requests established: %d", nl_socket_get_local_port (priv->nlh));
+
+	/* Initialize netlink socket for events */
+	priv->nlh_event = setup_socket (TRUE, platform);
+	g_assert (priv->nlh_event);
+	/* The default buffer size wasn't enough for the testsuites. It might just
+	 * as well happen with NetworkManager itself. For now let's hope 128KB is
+	 * good enough.
+	 */
+	nle = nl_socket_set_buffer_size (priv->nlh_event, 131072, 0);
+	g_assert (!nle);
+	nle = nl_socket_add_memberships (priv->nlh_event,
+	                                 RTNLGRP_LINK,
+	                                 RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR,
+	                                 RTNLGRP_IPV4_ROUTE,  RTNLGRP_IPV6_ROUTE,
+	                                 0);
+	g_assert (!nle);
+	debug ("Netlink socket for events established: %d", nl_socket_get_local_port (priv->nlh_event));
+
+	priv->event_channel = g_io_channel_unix_new (nl_socket_get_fd (priv->nlh_event));
+	g_io_channel_set_encoding (priv->event_channel, NULL, NULL);
+	g_io_channel_set_close_on_unref (priv->event_channel, TRUE);
+
+	channel_flags = g_io_channel_get_flags (priv->event_channel);
+	status = g_io_channel_set_flags (priv->event_channel,
+		channel_flags | G_IO_FLAG_NONBLOCK, NULL);
+	g_assert (status);
+	priv->event_id = g_io_add_watch (priv->event_channel,
+		(EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS),
+		event_handler, platform);
+
+	/* Allocate netlink caches */
+	rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &priv->link_cache);
+	rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache);
+	rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache);
+	g_assert (priv->link_cache && priv->address_cache && priv->route_cache);
+
+	for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object))
+		_rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object);
+
+	/* Set up udev monitoring */
+	priv->udev_client = g_udev_client_new (udev_subsys);
+	g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform);
+	priv->udev_devices = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
+
+	/* And read initial device list */
+	enumerator = g_udev_enumerator_new (priv->udev_client);
+	g_udev_enumerator_add_match_subsystem (enumerator, "net");
+	g_udev_enumerator_add_match_is_initialized (enumerator);
+
+	devices = g_udev_enumerator_execute (enumerator);
+	for (iter = devices; iter; iter = g_list_next (iter)) {
+		udev_device_added (platform, G_UDEV_DEVICE (iter->data));
+		g_object_unref (G_UDEV_DEVICE (iter->data));
+	}
+	g_list_free (devices);
+	g_object_unref (enumerator);
+
+	/* request all IPv6 addresses (hopeing that there is at least one), to check for
+	 * the IFA_FLAGS attribute. */
+	nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP);
+	if (nle < 0)
+		nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle));
+
+	priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit);
+
+	return TRUE;
+}
+
+static void
+nm_linux_platform_finalize (GObject *object)
+{
+	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object);
+
+	/* Free netlink resources */
+	g_source_remove (priv->event_id);
+	g_io_channel_unref (priv->event_channel);
+	nl_socket_free (priv->nlh);
+	nl_socket_free (priv->nlh_event);
+	nl_cache_free (priv->link_cache);
+	nl_cache_free (priv->address_cache);
+	nl_cache_free (priv->route_cache);
+
+	g_object_unref (priv->udev_client);
+	g_hash_table_unref (priv->udev_devices);
+	g_hash_table_unref (priv->wifi_data);
+
+	G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object);
+}
+
+#define OVERRIDE(function) platform_class->function = function
+
+static void
+nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMPlatformClass *platform_class = NM_PLATFORM_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMLinuxPlatformPrivate));
+
+	/* virtual methods */
+	object_class->finalize = nm_linux_platform_finalize;
+
+	platform_class->setup = setup;
+
+	platform_class->sysctl_set = sysctl_set;
+	platform_class->sysctl_get = sysctl_get;
+
+	platform_class->link_get = _nm_platform_link_get;
+	platform_class->link_get_all = link_get_all;
+	platform_class->link_add = link_add;
+	platform_class->link_delete = link_delete;
+	platform_class->link_get_ifindex = link_get_ifindex;
+	platform_class->link_get_name = link_get_name;
+	platform_class->link_get_type = link_get_type;
+	platform_class->link_get_type_name = link_get_type_name;
+
+	platform_class->link_refresh = link_refresh;
+
+	platform_class->link_set_up = link_set_up;
+	platform_class->link_set_down = link_set_down;
+	platform_class->link_set_arp = link_set_arp;
+	platform_class->link_set_noarp = link_set_noarp;
+	platform_class->link_is_up = link_is_up;
+	platform_class->link_is_connected = link_is_connected;
+	platform_class->link_uses_arp = link_uses_arp;
+
+	platform_class->link_get_address = link_get_address;
+	platform_class->link_set_address = link_set_address;
+	platform_class->link_get_mtu = link_get_mtu;
+	platform_class->link_set_mtu = link_set_mtu;
+
+	platform_class->link_get_physical_port_id = link_get_physical_port_id;
+	platform_class->link_get_wake_on_lan = link_get_wake_on_lan;
+
+	platform_class->link_supports_carrier_detect = link_supports_carrier_detect;
+	platform_class->link_supports_vlans = link_supports_vlans;
+
+	platform_class->link_enslave = link_enslave;
+	platform_class->link_release = link_release;
+	platform_class->link_get_master = link_get_master;
+	platform_class->master_set_option = master_set_option;
+	platform_class->master_get_option = master_get_option;
+	platform_class->slave_set_option = slave_set_option;
+	platform_class->slave_get_option = slave_get_option;
+
+	platform_class->vlan_add = vlan_add;
+	platform_class->vlan_get_info = vlan_get_info;
+	platform_class->vlan_set_ingress_map = vlan_set_ingress_map;
+	platform_class->vlan_set_egress_map = vlan_set_egress_map;
+
+	platform_class->infiniband_partition_add = infiniband_partition_add;
+
+	platform_class->veth_get_properties = veth_get_properties;
+	platform_class->tun_get_properties = tun_get_properties;
+	platform_class->macvlan_get_properties = macvlan_get_properties;
+	platform_class->vxlan_get_properties = vxlan_get_properties;
+	platform_class->gre_get_properties = gre_get_properties;
+
+	platform_class->wifi_get_capabilities = wifi_get_capabilities;
+	platform_class->wifi_get_bssid = wifi_get_bssid;
+	platform_class->wifi_get_ssid = wifi_get_ssid;
+	platform_class->wifi_get_frequency = wifi_get_frequency;
+	platform_class->wifi_get_quality = wifi_get_quality;
+	platform_class->wifi_get_rate = wifi_get_rate;
+	platform_class->wifi_get_mode = wifi_get_mode;
+	platform_class->wifi_set_mode = wifi_set_mode;
+	platform_class->wifi_find_frequency = wifi_find_frequency;
+	platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running;
+
+	platform_class->mesh_get_channel = mesh_get_channel;
+	platform_class->mesh_set_channel = mesh_set_channel;
+	platform_class->mesh_set_ssid = mesh_set_ssid;
+
+	platform_class->ip4_address_get_all = ip4_address_get_all;
+	platform_class->ip6_address_get_all = ip6_address_get_all;
+	platform_class->ip4_address_add = ip4_address_add;
+	platform_class->ip6_address_add = ip6_address_add;
+	platform_class->ip4_address_delete = ip4_address_delete;
+	platform_class->ip6_address_delete = ip6_address_delete;
+	platform_class->ip4_address_exists = ip4_address_exists;
+	platform_class->ip6_address_exists = ip6_address_exists;
+
+	platform_class->ip4_route_get_all = ip4_route_get_all;
+	platform_class->ip6_route_get_all = ip6_route_get_all;
+	platform_class->ip4_route_add = ip4_route_add;
+	platform_class->ip6_route_add = ip6_route_add;
+	platform_class->ip4_route_delete = ip4_route_delete;
+	platform_class->ip6_route_delete = ip6_route_delete;
+	platform_class->ip4_route_exists = ip4_route_exists;
+	platform_class->ip6_route_exists = ip6_route_exists;
+
+	platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags;
+}
diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h
new file mode 100644
index 00000000..53ea75e6
--- /dev/null
+++ b/src/platform/nm-linux-platform.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-linux-platform.h - Linux kernel & udev network configuration layer
+ *
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_LINUX_PLATFORM_H
+#define NM_LINUX_PLATFORM_H
+
+#include "nm-platform.h"
+
+#define NM_TYPE_LINUX_PLATFORM            (nm_linux_platform_get_type ())
+#define NM_LINUX_PLATFORM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatform))
+#define NM_LINUX_PLATFORM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass))
+#define NM_IS_LINUX_PLATFORM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_LINUX_PLATFORM))
+#define NM_IS_LINUX_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_LINUX_PLATFORM))
+#define NM_LINUX_PLATFORM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass))
+
+/******************************************************************/
+
+typedef struct {
+	NMPlatform parent;
+} NMLinuxPlatform;
+
+typedef struct {
+	NMPlatformClass parent;
+} NMLinuxPlatformClass;
+
+/******************************************************************/
+
+GType nm_linux_platform_get_type (void);
+
+void nm_linux_platform_setup (void);
+
+#endif /* NM_LINUX_PLATFORM_H */
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
new file mode 100644
index 00000000..542b0faa
--- /dev/null
+++ b/src/platform/nm-platform.c
@@ -0,0 +1,2618 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-platform.c - Handle runtime kernel networking configuration
+ *
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <netlink/route/addr.h>
+
+#include "NetworkManagerUtils.h"
+#include "nm-utils.h"
+#include "nm-platform.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+#include "nm-enum-types.h"
+
+#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__)
+
+#define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate))
+
+G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT)
+
+/* NMPlatform signals */
+enum {
+	SIGNAL_LINK_CHANGED,
+	SIGNAL_IP4_ADDRESS_CHANGED,
+	SIGNAL_IP6_ADDRESS_CHANGED,
+	SIGNAL_IP4_ROUTE_CHANGED,
+	SIGNAL_IP6_ROUTE_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/******************************************************************/
+
+/* Singleton NMPlatform subclass instance and cached class object */
+static NMPlatform *platform = NULL;
+static NMPlatformClass *klass = NULL;
+
+/**
+ * nm_platform_setup:
+ * @type: The #GType for a subclass of #NMPlatform
+ *
+ * Do not use this function directly, it is intended to be called by
+ * NMPlatform subclasses. For the linux platform initialization use
+ * nm_linux_platform_setup() instead.
+ *
+ * Failing to set up #NMPlatform singleton results in a fatal error,
+ * as well as trying to initialize it multiple times without freeing
+ * it.
+ *
+ * NetworkManager will typically use only one platform object during
+ * its run. Test programs might want to switch platform implementations,
+ * though. This is done with a combination of nm_platform_free() and
+ * nm_*_platform_setup().
+ */
+void
+nm_platform_setup (GType type)
+{
+	gboolean status;
+
+	g_assert (platform == NULL);
+
+	platform = g_object_new (type, NULL);
+	g_assert (NM_IS_PLATFORM (platform));
+
+	klass = NM_PLATFORM_GET_CLASS (platform);
+	g_assert (klass->setup);
+
+	status = klass->setup (platform);
+	g_assert (status);
+}
+
+/**
+ * nm_platform_free:
+ *
+ * Free #NMPlatform singleton created by nm_*_platform_setup().
+ */
+void
+nm_platform_free (void)
+{
+	g_assert (platform);
+
+	g_object_unref (platform);
+	platform = NULL;
+}
+
+/**
+ * nm_platform_get:
+ *
+ * Retrieve #NMPlatform singleton. Use this whenever you want to connect to
+ * #NMPlatform signals. It is an error to call it before nm_*_platform_setup()
+ * or after nm_platform_free().
+ *
+ * Returns: (transfer none): The #NMPlatform singleton reference.
+ */
+NMPlatform *
+nm_platform_get (void)
+{
+	g_assert (platform);
+
+	return platform;
+}
+
+/******************************************************************/
+
+/**
+ * nm_platform_set_error:
+ * @error: The error code
+ *
+ * Convenience function to falsify platform->error. It can be used for example
+ * by functions that want to save the error, execute some operations and
+ * restore it.
+ */
+void nm_platform_set_error (NMPlatformError error)
+{
+	platform->error = error;
+}
+
+/**
+ * nm_platform_get_error:
+ *
+ * Convenience function to quickly retrieve the error code of the last
+ * operation.
+ *
+ * Returns: Integer error code.
+ */
+NMPlatformError
+nm_platform_get_error (void)
+{
+	g_assert (platform);
+
+	return platform->error;
+}
+
+/**
+ * nm_platform_get_error_message:
+ *
+ * Returns: Static human-readable string for the error. Don't free.
+ */
+const char *
+nm_platform_get_error_msg (void)
+{
+	g_assert (platform);
+
+	switch (platform->error) {
+	case NM_PLATFORM_ERROR_NONE:
+		return "unknown error";
+	case NM_PLATFORM_ERROR_NOT_FOUND:
+		return "object not found";
+	case NM_PLATFORM_ERROR_EXISTS:
+		return "object already exists";
+	case NM_PLATFORM_ERROR_WRONG_TYPE:
+		return "object is wrong type";
+	case NM_PLATFORM_ERROR_NOT_SLAVE:
+		return "link not a slave";
+	case NM_PLATFORM_ERROR_NO_FIRMWARE:
+		return "firmware not found";
+	default:
+		return "invalid error number";
+	}
+}
+
+static void
+reset_error (void)
+{
+	g_assert (platform);
+	platform->error = NM_PLATFORM_ERROR_NONE;
+}
+
+#define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr"
+#define IFA_F_NOPREFIXROUTE_STR "noprefixroute"
+gboolean
+nm_platform_check_support_libnl_extended_ifa_flags ()
+{
+	static int supported = -1;
+
+	/* support for extended ifa-flags was added together
+	 * with the IFA_F_MANAGETEMPADDR flag. So, check if libnl
+	 * is able to parse this flag. */
+	if (supported == -1)
+		supported = rtnl_addr_str2flags (IFA_F_MANAGETEMPADDR_STR) == IFA_F_MANAGETEMPADDR;
+
+	return supported;
+}
+
+gboolean
+nm_platform_check_support_kernel_extended_ifa_flags ()
+{
+	g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE);
+
+	if (!klass->check_support_kernel_extended_ifa_flags)
+		return FALSE;
+
+	return klass->check_support_kernel_extended_ifa_flags (platform);
+}
+
+/******************************************************************/
+
+/**
+ * nm_platform_sysctl_set:
+ * @path: Absolute option path
+ * @value: Value to write
+ *
+ * This function is intended to be used for writing values to sysctl-style
+ * virtual runtime configuration files. This includes not only /proc/sys
+ * but also for example /sys/class.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+nm_platform_sysctl_set (const char *path, const char *value)
+{
+	reset_error ();
+
+	g_return_val_if_fail (path, FALSE);
+	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (klass->sysctl_set, FALSE);
+
+	return klass->sysctl_set (platform, path, value);
+}
+
+/**
+ * nm_platform_sysctl_get:
+ * @path: Absolute path to sysctl
+ *
+ * Returns: (transfer full): Contents of the virtual sysctl file.
+ */
+char *
+nm_platform_sysctl_get (const char *path)
+{
+	reset_error ();
+
+	g_return_val_if_fail (path, NULL);
+	g_return_val_if_fail (klass->sysctl_get, NULL);
+
+	return klass->sysctl_get (platform, path);
+}
+
+/**
+ * nm_platform_sysctl_get_int32:
+ * @path: Absolute path to sysctl
+ * @fallback: default value, if the content of path could not be read
+ * as decimal integer.
+ *
+ * Returns: contents of the sysctl file parsed as s32 integer, or
+ * @fallback on error. On error, %errno will be set to a non-zero
+ * value, on success %errno will be set to zero.
+ */
+gint32
+nm_platform_sysctl_get_int32 (const char *path, gint32 fallback)
+{
+	return nm_platform_sysctl_get_int_checked (path, 10, G_MININT32, G_MAXINT32, fallback);
+}
+
+/**
+ * nm_platform_sysctl_get_int_checked:
+ * @path: Absolute path to sysctl
+ * @base: base of numeric conversion
+ * @min: minimal value that is still valid
+ * @max: maximal value that is still valid
+ * @fallback: default value, if the content of path could not be read
+ * as valid integer.
+ *
+ * Returns: contents of the sysctl file parsed as s64 integer, or
+ * @fallback on error. On error, %errno will be set to a non-zero
+ * value. On success, %errno will be set to zero. The returned value
+ * will always be in the range between @min and @max
+ * (inclusive) or @fallback.
+ */
+gint64
+nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gint64 max, gint64 fallback)
+{
+	char *value = NULL;
+	gint32 ret;
+
+	g_return_val_if_fail (path, fallback);
+
+	if (path)
+		value = nm_platform_sysctl_get (path);
+
+	if (!value) {
+		errno = EINVAL;
+		return fallback;
+	}
+
+	ret = nm_utils_ascii_str_to_int64 (value, base, min, max, fallback);
+	g_free (value);
+	return ret;
+}
+
+/******************************************************************/
+
+/**
+ * nm_platform_query_devices:
+ *
+ * Emit #NMPlatform:link-changed ADDED signals for all currently-known links.
+ * Should only be called at startup.
+ */
+void
+nm_platform_query_devices (void)
+{
+	GArray *links_array;
+	NMPlatformLink *links;
+	int i;
+
+	links_array = nm_platform_link_get_all ();
+	links = (NMPlatformLink *) links_array->data;
+	for (i = 0; i < links_array->len; i++) {
+		g_signal_emit (platform, signals[SIGNAL_LINK_CHANGED], 0,
+		               links[i].ifindex, &links[i], NM_PLATFORM_SIGNAL_ADDED,
+		               NM_PLATFORM_REASON_INTERNAL);
+	}
+	g_array_unref (links_array);
+}
+
+/**
+ * nm_platform_link_get_all:
+ *
+ * Retrieve a snapshot of configuration for all links at once. The result is
+ * owned by the caller and should be freed with g_array_unref().
+ */
+GArray *
+nm_platform_link_get_all (void)
+{
+	GArray *links, *result;
+	guint i, j, nresult;
+	GHashTable *unseen;
+	NMPlatformLink *item;
+
+	reset_error ();
+
+	g_return_val_if_fail (klass->link_get_all, NULL);
+
+	links = klass->link_get_all (platform);
+
+	if (!links || links->len == 0)
+		return links;
+
+	unseen = g_hash_table_new (g_direct_hash, g_direct_equal);
+	for (i = 0; i < links->len; i++) {
+		item = &g_array_index (links, NMPlatformLink, i);
+
+		if (item->ifindex <= 0 || g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))) {
+			g_warn_if_reached ();
+			item->ifindex = 0;
+			continue;
+		}
+
+		g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL);
+	}
+
+#ifndef G_DISABLE_ASSERT
+	/* Ensure that link_get_all returns a consistent and valid result. */
+	for (i = 0; i < links->len; i++) {
+		item = &g_array_index (links, NMPlatformLink, i);
+
+		if (!item->ifindex)
+			continue;
+		if (item->master != 0) {
+			g_warn_if_fail (item->master > 0);
+			g_warn_if_fail (item->master != item->ifindex);
+			g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->master)));
+		}
+		if (item->parent != 0) {
+			g_warn_if_fail (item->parent > 0);
+			g_warn_if_fail (item->parent != item->ifindex);
+			g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)));
+		}
+	}
+#endif
+
+	/* Re-order the links list such that children/slaves come after all ancestors */
+	nresult = g_hash_table_size (unseen);
+	result = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), nresult);
+	g_array_set_size (result, nresult);
+
+	j = 0;
+	do {
+		gboolean found_something = FALSE;
+		guint first_idx = G_MAXUINT;
+
+		for (i = 0; i < links->len; i++) {
+			item = &g_array_index (links, NMPlatformLink, i);
+
+			if (!item->ifindex)
+				continue;
+
+			if (first_idx == G_MAXUINT)
+				first_idx = i;
+
+			g_assert (g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex)));
+
+			if (item->master > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->master)))
+				continue;
+			if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)))
+				continue;
+
+			g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex));
+			g_array_index (result, NMPlatformLink, j++) = *item;
+			item->ifindex = 0;
+			found_something = TRUE;
+		}
+
+		if (!found_something) {
+			/* there is a circle, pop the first (remaining) element from the list */
+			g_warn_if_reached ();
+			item = &g_array_index (links, NMPlatformLink, first_idx);
+
+			g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex));
+			g_array_index (result, NMPlatformLink, j++) = *item;
+			item->ifindex = 0;
+		}
+	} while (j < nresult);
+
+	g_hash_table_destroy (unseen);
+	g_array_free (links, TRUE);
+
+	return result;
+}
+
+/**
+ * nm_platform_link_get:
+ * @ifindex: ifindex of the link
+ * @link: (out): output NMPlatformLink structure.
+ *
+ * If a link with given @ifindex exists, fill the given NMPlatformLink
+ * structure.
+ *
+ * Returns: %TRUE, if such a link exists, %FALSE otherwise.
+ * If the link does not exist, the content of @link is undefined.
+ **/
+gboolean
+nm_platform_link_get (int ifindex, NMPlatformLink *link)
+{
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (link, FALSE);
+
+	g_return_val_if_fail (klass->link_get, FALSE);
+	return !!klass->link_get (platform, ifindex, link);
+}
+
+/**
+ * nm_platform_link_add:
+ * @name: Interface name
+ * @type: Interface type
+ * @address: (allow-none): set the mac address of the link
+ * @address_len: the length of the @address
+ *
+ * Add a software interface. Sets platform->error to NM_PLATFORM_ERROR_EXISTS
+ * if interface is already already exists.  Any link-changed ADDED signal will be
+ * emitted directly, before this function finishes.
+ */
+static gboolean
+nm_platform_link_add (const char *name, NMLinkType type, const void *address, size_t address_len)
+{
+	reset_error ();
+
+	g_return_val_if_fail (name, FALSE);
+	g_return_val_if_fail (klass->link_add, FALSE);
+	g_return_val_if_fail ( (address != NULL) ^ (address_len == 0) , FALSE);
+
+	if (nm_platform_link_exists (name)) {
+		debug ("link: already exists");
+		platform->error = NM_PLATFORM_ERROR_EXISTS;
+		return FALSE;
+	}
+
+	return klass->link_add (platform, name, type, address, address_len);
+}
+
+/**
+ * nm_platform_dummy_add:
+ * @name: New interface name
+ *
+ * Create a software ethernet-like interface
+ */
+gboolean
+nm_platform_dummy_add (const char *name)
+{
+	g_return_val_if_fail (name, FALSE);
+
+	debug ("link: adding dummy '%s'", name);
+	return nm_platform_link_add (name, NM_LINK_TYPE_DUMMY, NULL, 0);
+}
+
+/**
+ * nm_platform_link_exists:
+ * @name: Interface name
+ *
+ * Returns: %TRUE if an interface of this name exists, %FALSE otherwise.
+ */
+gboolean
+nm_platform_link_exists (const char *name)
+{
+	int ifindex = nm_platform_link_get_ifindex (name);
+
+	reset_error();
+	return ifindex > 0;
+}
+
+/**
+ * nm_platform_link_delete:
+ * @ifindex: Interface index
+ *
+ * Delete a software interface. Sets platform->error to
+ * NM_PLATFORM_ERROR_NOT_FOUND if ifindex not available.
+ */
+gboolean
+nm_platform_link_delete (int ifindex)
+{
+	const char *name;
+
+	reset_error ();
+
+	g_return_val_if_fail (klass->link_delete, FALSE);
+
+	name = nm_platform_link_get_name (ifindex);
+
+	if (!name)
+		return FALSE;
+
+	debug ("link: deleting '%s' (%d)", name, ifindex);
+	return klass->link_delete (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_get_index:
+ * @name: Interface name
+ *
+ * Returns: The interface index corresponding to the given interface name
+ * or 0. Inteface name is owned by #NMPlatform, don't free it.
+ */
+int
+nm_platform_link_get_ifindex (const char *name)
+{
+	int ifindex;
+
+	reset_error ();
+
+	g_return_val_if_fail (name, 0);
+	g_return_val_if_fail (klass->link_get_ifindex, 0);
+
+	ifindex = klass->link_get_ifindex (platform, name);
+
+	if (!ifindex) {
+		debug ("link not found: %s", name);
+		platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
+	}
+
+	return ifindex;
+}
+
+/**
+ * nm_platform_link_get_name:
+ * @name: Interface name
+ *
+ * Returns: The interface name corresponding to the given interface index
+ * or %NULL.
+ */
+const char *
+nm_platform_link_get_name (int ifindex)
+{
+	const char *name;
+
+	reset_error ();
+
+	g_return_val_if_fail (klass->link_get_name, NULL);
+
+	name = klass->link_get_name (platform, ifindex);
+
+	if (!name) {
+		debug ("link not found: %d", ifindex);
+		platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
+		return FALSE;
+	}
+
+	return name;
+}
+
+/**
+ * nm_platform_link_get_type:
+ * @ifindex: Interface index.
+ *
+ * Returns: Link type constant as defined in nm-platform.h. On error,
+ * NM_LINK_TYPE_NONE is returned.
+ */
+NMLinkType
+nm_platform_link_get_type (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (klass->link_get_type, NM_LINK_TYPE_NONE);
+
+	return klass->link_get_type (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_get_type_name:
+ * @ifindex: Interface index.
+ *
+ * Returns: A string describing the type of link. In some cases this
+ * may be more specific than nm_platform_link_get_type(), but in
+ * other cases it may not. On error, %NULL is returned.
+ */
+const char *
+nm_platform_link_get_type_name (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (klass->link_get_type_name, NULL);
+
+	return klass->link_get_type_name (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_is_software:
+ * @ifindex: Interface index.
+ *
+ * Returns: %TRUE if ifindex belongs to a software interface, not backed by
+ * a physical device.
+ */
+gboolean
+nm_platform_link_is_software (int ifindex)
+{
+	return (nm_platform_link_get_type (ifindex) & 0x10000);
+}
+
+/**
+ * nm_platform_link_supports_slaves:
+ * @ifindex: Interface index.
+ *
+ * Returns: %TRUE if ifindex belongs to an interface capable of enslaving
+ * other interfaces.
+ */
+gboolean
+nm_platform_link_supports_slaves (int ifindex)
+{
+	return (nm_platform_link_get_type (ifindex) & 0x20000);
+}
+
+/**
+ * nm_platform_link_refresh:
+ * @ifindex: Interface index
+ *
+ * Reload the cache for ifindex synchronously.
+ */
+gboolean
+nm_platform_link_refresh (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+
+	if (klass->link_refresh)
+		return klass->link_refresh (platform, ifindex);
+
+	return TRUE;
+}
+
+/**
+ * nm_platform_link_is_up:
+ * @ifindex: Interface index
+ *
+ * Check if the interface is up.
+ */
+gboolean
+nm_platform_link_is_up (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (klass->link_is_up, FALSE);
+
+	return klass->link_is_up (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_is_connected:
+ * @ifindex: Interface index
+ *
+ * Check if the interface is connected.
+ */
+gboolean
+nm_platform_link_is_connected (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (klass->link_is_connected, FALSE);
+
+	return klass->link_is_connected (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_uses_arp:
+ * @ifindex: Interface index
+ *
+ * Check if the interface is configured to use ARP.
+ */
+gboolean
+nm_platform_link_uses_arp (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (klass->link_uses_arp, FALSE);
+
+	return klass->link_uses_arp (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_set_address:
+ * @ifindex: Interface index
+ * @address: The new MAC address
+ *
+ * Set interface MAC address.
+ */
+gboolean
+nm_platform_link_set_address (int ifindex, gconstpointer address, size_t length)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (address, FALSE);
+	g_return_val_if_fail (length > 0, FALSE);
+	g_return_val_if_fail (klass->link_set_address, FALSE);
+
+	debug ("link: setting '%s' (%d) hardware address", nm_platform_link_get_name (ifindex), ifindex);
+	return klass->link_set_address (platform, ifindex, address, length);
+}
+
+/**
+ * nm_platform_link_get_address:
+ * @ifindex: Interface index
+ * @length: Pointer to a variable to store address length
+ *
+ * Saves interface hardware address to @address.
+ */
+gconstpointer
+nm_platform_link_get_address (int ifindex, size_t *length)
+{
+	reset_error ();
+
+	if (length)
+		*length = 0;
+
+	g_return_val_if_fail (ifindex > 0, NULL);
+	g_return_val_if_fail (klass->link_get_address, NULL);
+
+	return klass->link_get_address (platform, ifindex, length);
+}
+
+gboolean
+nm_platform_link_supports_carrier_detect (int ifindex)
+{
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (klass->link_supports_carrier_detect, FALSE);
+
+	return klass->link_supports_carrier_detect (platform, ifindex);
+}
+
+gboolean
+nm_platform_link_supports_vlans (int ifindex)
+{
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (klass->link_supports_vlans, FALSE);
+
+	return klass->link_supports_vlans (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_set_up:
+ * @ifindex: Interface index
+ *
+ * Bring the interface up.
+ */
+gboolean
+nm_platform_link_set_up (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (klass->link_set_up, FALSE);
+
+	debug ("link: setting up '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex);
+	return klass->link_set_up (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_set_down:
+ * @ifindex: Interface index
+ *
+ * Take the interface down.
+ */
+gboolean
+nm_platform_link_set_down (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (klass->link_set_down, FALSE);
+
+	debug ("link: setting down '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex);
+	return klass->link_set_down (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_set_arp:
+ * @ifindex: Interface index
+ *
+ * Enable ARP on the interface.
+ */
+gboolean
+nm_platform_link_set_arp (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (klass->link_set_arp, FALSE);
+
+	debug ("link: setting arp '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex);
+	return klass->link_set_arp (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_set_noarp:
+ * @ifindex: Interface index
+ *
+ * Disable ARP on the interface.
+ */
+gboolean
+nm_platform_link_set_noarp (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (klass->link_set_noarp, FALSE);
+
+	debug ("link: setting noarp '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex);
+	return klass->link_set_noarp (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_set_mtu:
+ * @ifindex: Interface index
+ * @mtu: The new MTU value
+ *
+ * Set interface MTU.
+ */
+gboolean
+nm_platform_link_set_mtu (int ifindex, guint32 mtu)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (mtu > 0, FALSE);
+	g_return_val_if_fail (klass->link_set_mtu, FALSE);
+
+	debug ("link: setting '%s' (%d) mtu %d", nm_platform_link_get_name (ifindex), ifindex, mtu);
+	return klass->link_set_mtu (platform, ifindex, mtu);
+}
+
+/**
+ * nm_platform_link_get_mtu:
+ * @ifindex: Interface index
+ *
+ * Returns: MTU value for the interface or 0 on error.
+ */
+guint32
+nm_platform_link_get_mtu (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex >= 0, 0);
+	g_return_val_if_fail (klass->link_get_mtu, 0);
+
+	return klass->link_get_mtu (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_get_mtu:
+ * @ifindex: Interface index
+ *
+ * Returns: physical port ID for the interface, or %NULL on error
+ * or if the interface has no physical port ID.
+ */
+char *
+nm_platform_link_get_physical_port_id (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex >= 0, NULL);
+	g_return_val_if_fail (klass->link_get_physical_port_id, NULL);
+
+	return klass->link_get_physical_port_id (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_get_wake_onlan:
+ * @ifindex: Interface index
+ *
+ * Returns: the "Wake-on-LAN" status for @ifindex.
+ */
+gboolean
+nm_platform_link_get_wake_on_lan (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (klass->link_get_wake_on_lan, FALSE);
+
+	return klass->link_get_wake_on_lan (platform, ifindex);
+}
+
+/**
+ * nm_platform_link_enslave:
+ * @master: Interface index of the master
+ * @slave: Interface index of the slave
+ *
+ * Enslave @slave to @master.
+ */
+gboolean
+nm_platform_link_enslave (int master, int slave)
+{
+	reset_error ();
+
+	g_assert (platform);
+	g_return_val_if_fail (master > 0, FALSE);
+	g_return_val_if_fail (slave> 0, FALSE);
+	g_return_val_if_fail (klass->link_enslave, FALSE);
+
+	debug ("link: enslaving '%s' (%d) to master '%s' (%d)",
+		nm_platform_link_get_name (slave), slave,
+		nm_platform_link_get_name (master), master);
+	return klass->link_enslave (platform, master, slave);
+}
+
+/**
+ * nm_platform_link_release:
+ * @master: Interface index of the master
+ * @slave: Interface index of the slave
+ *
+ * Release @slave from @master.
+ */
+gboolean
+nm_platform_link_release (int master, int slave)
+{
+	reset_error ();
+
+	g_assert (platform);
+	g_return_val_if_fail (master > 0, FALSE);
+	g_return_val_if_fail (slave > 0, FALSE);
+	g_return_val_if_fail (klass->link_release, FALSE);
+
+	if (nm_platform_link_get_master (slave) != master) {
+		platform->error = NM_PLATFORM_ERROR_NOT_SLAVE;
+		return FALSE;
+	}
+
+	debug ("link: releasing '%s' (%d) from master '%s' (%d)",
+		nm_platform_link_get_name (slave), slave,
+		nm_platform_link_get_name (master), master);
+	return klass->link_release (platform, master, slave);
+}
+
+/**
+ * nm_platform_link_get_master:
+ * @slave: Interface index of the slave.
+ *
+ * Returns: Interfase index of the slave's master.
+ */
+int
+nm_platform_link_get_master (int slave)
+{
+	reset_error ();
+
+	g_assert (platform);
+	g_return_val_if_fail (slave >= 0, FALSE);
+	g_return_val_if_fail (klass->link_get_master, FALSE);
+
+	if (!nm_platform_link_get_name (slave)) {
+		platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
+		return 0;
+	}
+	return klass->link_get_master (platform, slave);
+}
+
+/**
+ * nm_platform_bridge_add:
+ * @name: New interface name
+ * @address: (allow-none): set the mac address of the new bridge
+ * @address_len: the length of the @address
+ *
+ * Create a software bridge.
+ */
+gboolean
+nm_platform_bridge_add (const char *name, const void *address, size_t address_len)
+{
+	debug ("link: adding bridge '%s'", name);
+	return nm_platform_link_add (name, NM_LINK_TYPE_BRIDGE, address, address_len);
+}
+
+/**
+ * nm_platform_bond_add:
+ * @name: New interface name
+ *
+ * Create a software bonding device.
+ */
+gboolean
+nm_platform_bond_add (const char *name)
+{
+	debug ("link: adding bond '%s'", name);
+	return nm_platform_link_add (name, NM_LINK_TYPE_BOND, NULL, 0);
+}
+
+/**
+ * nm_platform_team_add:
+ * @name: New interface name
+ *
+ * Create a software teaming device.
+ */
+gboolean
+nm_platform_team_add (const char *name)
+{
+	debug ("link: adding team '%s'", name);
+	return nm_platform_link_add (name, NM_LINK_TYPE_TEAM, NULL, 0);
+}
+
+/**
+ * nm_platform_vlan_add:
+ * @name: New interface name
+ * @vlanid: VLAN identifier
+ * @vlanflags: VLAN flags from libnm-util
+ *
+ * Create a software VLAN device.
+ */
+gboolean
+nm_platform_vlan_add (const char *name, int parent, int vlanid, guint32 vlanflags)
+{
+	reset_error ();
+
+	g_assert (platform);
+	g_return_val_if_fail (parent >= 0, FALSE);
+	g_return_val_if_fail (vlanid >= 0, FALSE);
+	g_return_val_if_fail (name, FALSE);
+	g_return_val_if_fail (klass->vlan_add, FALSE);
+
+	if (nm_platform_link_exists (name)) {
+		debug ("link already exists: %s", name);
+		platform->error = NM_PLATFORM_ERROR_EXISTS;
+		return FALSE;
+	}
+
+	debug ("link: adding vlan '%s' parent %d vlanid %d vlanflags %x",
+		name, parent, vlanid, vlanflags);
+	return klass->vlan_add (platform, name, parent, vlanid, vlanflags);
+}
+
+gboolean
+nm_platform_master_set_option (int ifindex, const char *option, const char *value)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (option, FALSE);
+	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (klass->master_set_option, FALSE);
+
+	return klass->master_set_option (platform, ifindex, option, value);
+}
+
+char *
+nm_platform_master_get_option (int ifindex, const char *option)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (option, FALSE);
+	g_return_val_if_fail (klass->master_set_option, FALSE);
+
+	return klass->master_get_option (platform, ifindex, option);
+}
+
+gboolean
+nm_platform_slave_set_option (int ifindex, const char *option, const char *value)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (option, FALSE);
+	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (klass->slave_set_option, FALSE);
+
+	return klass->slave_set_option (platform, ifindex, option, value);
+}
+
+char *
+nm_platform_slave_get_option (int ifindex, const char *option)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (option, FALSE);
+	g_return_val_if_fail (klass->slave_set_option, FALSE);
+
+	return klass->slave_get_option (platform, ifindex, option);
+}
+
+gboolean
+nm_platform_vlan_get_info (int ifindex, int *parent, int *vlanid)
+{
+	reset_error ();
+
+	g_assert (platform);
+	g_return_val_if_fail (klass->vlan_get_info, FALSE);
+
+	if (parent)
+		*parent = 0;
+	if (vlanid)
+		*vlanid = 0;
+
+	if (nm_platform_link_get_type (ifindex) != NM_LINK_TYPE_VLAN)
+		return FALSE;
+
+	return klass->vlan_get_info (platform, ifindex, parent, vlanid);
+}
+
+gboolean
+nm_platform_vlan_set_ingress_map (int ifindex, int from, int to)
+{
+	reset_error ();
+
+	g_assert (platform);
+	g_return_val_if_fail (klass->vlan_set_ingress_map, FALSE);
+
+	debug ("link: setting vlan ingress map for %d from %d to %d", ifindex, from, to);
+	return klass->vlan_set_ingress_map (platform, ifindex, from, to);
+}
+
+gboolean
+nm_platform_vlan_set_egress_map (int ifindex, int from, int to)
+{
+	reset_error ();
+
+	g_assert (platform);
+	g_return_val_if_fail (klass->vlan_set_egress_map, FALSE);
+
+	debug ("link: setting vlan egress map for %d from %d to %d", ifindex, from, to);
+	return klass->vlan_set_egress_map (platform, ifindex, from, to);
+}
+
+gboolean
+nm_platform_infiniband_partition_add (int parent, int p_key)
+{
+	const char *parent_name;
+	char *name;
+
+	reset_error ();
+
+	g_return_val_if_fail (parent >= 0, FALSE);
+	g_return_val_if_fail (p_key >= 0, FALSE);
+	g_return_val_if_fail (klass->infiniband_partition_add, FALSE);
+
+	if (nm_platform_link_get_type (parent) != NM_LINK_TYPE_INFINIBAND) {
+		platform->error = NM_PLATFORM_ERROR_WRONG_TYPE;
+		return FALSE;
+	}
+
+	parent_name = nm_platform_link_get_name (parent);
+	name = g_strdup_printf ("%s.%04x", parent_name, p_key);
+	if (nm_platform_link_exists (name)) {
+		debug ("infiniband: already exists");
+		platform->error = NM_PLATFORM_ERROR_EXISTS;
+		g_free (name);
+		return FALSE;
+	}
+	g_free (name);
+
+	return klass->infiniband_partition_add (platform, parent, p_key);
+}
+
+gboolean
+nm_platform_veth_get_properties (int ifindex, NMPlatformVethProperties *props)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (props != NULL, FALSE);
+
+	return klass->veth_get_properties (platform, ifindex, props);
+}
+
+gboolean
+nm_platform_tun_get_properties (int ifindex, NMPlatformTunProperties *props)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (props != NULL, FALSE);
+
+	return klass->tun_get_properties (platform, ifindex, props);
+}
+
+gboolean
+nm_platform_macvlan_get_properties (int ifindex, NMPlatformMacvlanProperties *props)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (props != NULL, FALSE);
+
+	return klass->macvlan_get_properties (platform, ifindex, props);
+}
+
+gboolean
+nm_platform_vxlan_get_properties (int ifindex, NMPlatformVxlanProperties *props)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (props != NULL, FALSE);
+
+	return klass->vxlan_get_properties (platform, ifindex, props);
+}
+
+gboolean
+nm_platform_gre_get_properties (int ifindex, NMPlatformGreProperties *props)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (props != NULL, FALSE);
+
+	return klass->gre_get_properties (platform, ifindex, props);
+}
+
+gboolean
+nm_platform_wifi_get_capabilities (int ifindex, NMDeviceWifiCapabilities *caps)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+
+	return klass->wifi_get_capabilities (platform, ifindex, caps);
+}
+
+gboolean
+nm_platform_wifi_get_bssid (int ifindex, struct ether_addr *bssid)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+
+	return klass->wifi_get_bssid (platform, ifindex, bssid);
+}
+
+GByteArray *
+nm_platform_wifi_get_ssid (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, NULL);
+
+	return klass->wifi_get_ssid (platform, ifindex);
+}
+
+guint32
+nm_platform_wifi_get_frequency (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, 0);
+
+	return klass->wifi_get_frequency (platform, ifindex);
+}
+
+int
+nm_platform_wifi_get_quality (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, 0);
+
+	return klass->wifi_get_quality (platform, ifindex);
+}
+
+guint32
+nm_platform_wifi_get_rate (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, 0);
+
+	return klass->wifi_get_rate (platform, ifindex);
+}
+
+NM80211Mode
+nm_platform_wifi_get_mode (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, NM_802_11_MODE_UNKNOWN);
+
+	return klass->wifi_get_mode (platform, ifindex);
+}
+
+void
+nm_platform_wifi_set_mode (int ifindex, NM80211Mode mode)
+{
+	reset_error ();
+
+	g_return_if_fail (ifindex > 0);
+
+	klass->wifi_set_mode (platform, ifindex, mode);
+}
+
+guint32
+nm_platform_wifi_find_frequency (int ifindex, const guint32 *freqs)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, 0);
+	g_return_val_if_fail (freqs != NULL, 0);
+
+	return klass->wifi_find_frequency (platform, ifindex, freqs);
+}
+
+void
+nm_platform_wifi_indicate_addressing_running (int ifindex, gboolean running)
+{
+	reset_error ();
+
+	g_return_if_fail (ifindex > 0);
+
+	klass->wifi_indicate_addressing_running (platform, ifindex, running);
+}
+
+guint32
+nm_platform_mesh_get_channel (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, 0);
+
+	return klass->mesh_get_channel (platform, ifindex);
+}
+
+gboolean
+nm_platform_mesh_set_channel (int ifindex, guint32 channel)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+
+	return klass->mesh_set_channel (platform, ifindex, channel);
+}
+
+gboolean
+nm_platform_mesh_set_ssid (int ifindex, const GByteArray *ssid)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (ssid != NULL, FALSE);
+
+	return klass->mesh_set_ssid (platform, ifindex, ssid);
+}
+
+/******************************************************************/
+
+GArray *
+nm_platform_ip4_address_get_all (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, NULL);
+	g_return_val_if_fail (klass->ip4_address_get_all, NULL);
+
+	return klass->ip4_address_get_all (platform, ifindex);
+}
+
+GArray *
+nm_platform_ip6_address_get_all (int ifindex)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, NULL);
+	g_return_val_if_fail (klass->ip6_address_get_all, NULL);
+
+	return klass->ip6_address_get_all (platform, ifindex);
+}
+
+gboolean
+nm_platform_ip4_address_add (int ifindex,
+                             in_addr_t address,
+                             in_addr_t peer_address,
+                             int plen,
+                             guint32 lifetime,
+                             guint32 preferred,
+                             const char *label)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (plen > 0, FALSE);
+	g_return_val_if_fail (lifetime > 0, FALSE);
+	g_return_val_if_fail (preferred <= lifetime, FALSE);
+	g_return_val_if_fail (klass->ip4_address_add, FALSE);
+	g_return_val_if_fail (!label || strlen (label) < sizeof (((NMPlatformIP4Address *) NULL)->label), FALSE);
+
+	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+		NMPlatformIP4Address addr = { 0 };
+
+		addr.ifindex = ifindex;
+		addr.address = address;
+		addr.peer_address = peer_address;
+		addr.plen = plen;
+		addr.timestamp = 0; /* set it at zero, which to_string will treat as *now* */
+		addr.lifetime = lifetime;
+		addr.preferred = preferred;
+		if (label)
+			g_strlcpy (addr.label, label, sizeof (addr.label));
+
+		debug ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr));
+	}
+	return klass->ip4_address_add (platform, ifindex, address, peer_address, plen, lifetime, preferred, label);
+}
+
+gboolean
+nm_platform_ip6_address_add (int ifindex,
+                             struct in6_addr address,
+                             struct in6_addr peer_address,
+                             int plen,
+                             guint32 lifetime,
+                             guint32 preferred,
+                             guint flags)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (plen > 0, FALSE);
+	g_return_val_if_fail (lifetime > 0, FALSE);
+	g_return_val_if_fail (preferred <= lifetime, FALSE);
+	g_return_val_if_fail (klass->ip6_address_add, FALSE);
+
+	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+		NMPlatformIP6Address addr = { 0 };
+
+		addr.ifindex = ifindex;
+		addr.address = address;
+		addr.peer_address = peer_address;
+		addr.plen = plen;
+		addr.timestamp = 0; /* set it to zero, which to_string will treat as *now* */
+		addr.lifetime = lifetime;
+		addr.preferred = preferred;
+		addr.flags = flags;
+
+		debug ("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string (&addr));
+	}
+	return klass->ip6_address_add (platform, ifindex, address, peer_address, plen, lifetime, preferred, flags);
+}
+
+gboolean
+nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (plen > 0, FALSE);
+	g_return_val_if_fail (klass->ip4_address_delete, FALSE);
+
+	debug ("address: deleting IPv4 address %s/%d", nm_utils_inet4_ntop (address, NULL), plen);
+	return klass->ip4_address_delete (platform, ifindex, address, plen);
+}
+
+gboolean
+nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (plen > 0, FALSE);
+	g_return_val_if_fail (klass->ip6_address_delete, FALSE);
+
+	debug ("address: deleting IPv6 address %s/%d", nm_utils_inet6_ntop (&address, NULL), plen);
+	return klass->ip6_address_delete (platform, ifindex, address, plen);
+}
+
+gboolean
+nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen)
+{
+	reset_error ();
+
+	g_return_val_if_fail (plen > 0, FALSE);
+	g_return_val_if_fail (klass->ip4_address_exists, FALSE);
+
+	return klass->ip4_address_exists (platform, ifindex, address, plen);
+}
+
+gboolean
+nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen)
+{
+	reset_error ();
+
+	g_return_val_if_fail (plen > 0, FALSE);
+	g_return_val_if_fail (klass->ip6_address_exists, FALSE);
+
+	return klass->ip6_address_exists (platform, ifindex, address, plen);
+}
+
+static gboolean
+array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address)
+{
+	guint len = addresses ? addresses->len : 0;
+	guint i;
+
+	for (i = 0; i < len; i++) {
+		NMPlatformIP4Address *candidate = &g_array_index (addresses, NMPlatformIP4Address, i);
+
+		if (candidate->address == address->address && candidate->plen == address->plen)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address)
+{
+	guint len = addresses ? addresses->len : 0;
+	guint i;
+
+	for (i = 0; i < len; i++) {
+		NMPlatformIP6Address *candidate = &g_array_index (addresses, NMPlatformIP6Address, i);
+
+		if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+/**
+ * Takes a pair @timestamp and @duration, and returns the remaining duration based
+ * on the new timestamp @now.
+ */
+static guint32
+_rebase_relative_time_on_now (guint32 timestamp, guint32 duration, guint32 now, guint32 padding)
+{
+	gint64 t;
+
+	if (duration == NM_PLATFORM_LIFETIME_PERMANENT)
+		return NM_PLATFORM_LIFETIME_PERMANENT;
+
+	if (timestamp == 0) {
+		/* if the @timestamp is zero, assume it was just left unset and that the relative
+		 * @duration starts counting from @now. This is convenient to construct an address
+		 * and print it in nm_platform_ip4_address_to_string().
+		 *
+		 * In general it does not make sense to set the @duration without anchoring at
+		 * @timestamp because you don't know the absolute expiration time when looking
+		 * at the address at a later moment. */
+		timestamp = now;
+	}
+
+	/* For timestamp > now, just accept it and calculate the expected(?) result. */
+	t = (gint64) timestamp + (gint64) duration - (gint64) now;
+
+	/* Optional padding to avoid potential races. */
+	t += (gint64) padding;
+
+	if (t <= 0)
+		return 0;
+	if (t >= NM_PLATFORM_LIFETIME_PERMANENT)
+		return NM_PLATFORM_LIFETIME_PERMANENT - 1;
+	return t;
+}
+
+static gboolean
+_address_get_lifetime (const NMPlatformIPAddress *address, guint32 now, guint32 padding, guint32 *out_lifetime, guint32 *out_preferred)
+{
+	gint32 lifetime, preferred;
+
+	if (address->lifetime == 0) {
+		*out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
+		*out_preferred = NM_PLATFORM_LIFETIME_PERMANENT;
+	} else {
+		lifetime = _rebase_relative_time_on_now (address->timestamp, address->lifetime, now, padding);
+		if (!lifetime)
+			return FALSE;
+		preferred = _rebase_relative_time_on_now (address->timestamp, address->preferred, now, padding);
+
+		*out_lifetime = lifetime;
+		*out_preferred = MIN (preferred, lifetime);
+
+		/* Assert that non-permanent addresses have a (positive) @timestamp. _rebase_relative_time_on_now()
+		 * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
+		 * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
+		 */
+		g_return_val_if_fail (   address->timestamp != 0
+		                      || (   address->lifetime  == NM_PLATFORM_LIFETIME_PERMANENT
+		                          && address->preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE);
+		g_return_val_if_fail (preferred <= lifetime, TRUE);
+	}
+	return TRUE;
+}
+
+/**
+ * nm_platform_ip4_address_sync:
+ * @ifindex: Interface index
+ * @known_addresses: List of addresses
+ *
+ * A convenience function to synchronize addresses for a specific interface
+ * with the least possible disturbance. It simply removes addresses that are
+ * not listed and adds addresses that are.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses)
+{
+	GArray *addresses;
+	NMPlatformIP4Address *address;
+	guint32 now = nm_utils_get_monotonic_timestamp_s ();
+	int i;
+
+	/* Delete unknown addresses */
+	addresses = nm_platform_ip4_address_get_all (ifindex);
+	for (i = 0; i < addresses->len; i++) {
+		address = &g_array_index (addresses, NMPlatformIP4Address, i);
+
+		if (!array_contains_ip4_address (known_addresses, address))
+			nm_platform_ip4_address_delete (ifindex, address->address, address->plen);
+	}
+	g_array_free (addresses, TRUE);
+
+	if (!known_addresses)
+		return TRUE;
+
+	/* Add missing addresses */
+	for (i = 0; i < known_addresses->len; i++) {
+		const NMPlatformIP4Address *known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i);
+		guint32 lifetime, preferred;
+
+		/* add a padding of 5 seconds to avoid potential races. */
+		if (!_address_get_lifetime ((NMPlatformIPAddress *) known_address, now, 5, &lifetime, &preferred))
+			continue;
+
+		if (!nm_platform_ip4_address_add (ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+/**
+ * nm_platform_ip6_address_sync:
+ * @ifindex: Interface index
+ * @known_addresses: List of addresses
+ *
+ * A convenience function to synchronize addresses for a specific interface
+ * with the least possible disturbance. It simply removes addresses that are
+ * not listed and adds addresses that are.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses)
+{
+	GArray *addresses;
+	NMPlatformIP6Address *address;
+	guint32 now = nm_utils_get_monotonic_timestamp_s ();
+	int i;
+
+	/* Delete unknown addresses */
+	addresses = nm_platform_ip6_address_get_all (ifindex);
+	for (i = 0; i < addresses->len; i++) {
+		address = &g_array_index (addresses, NMPlatformIP6Address, i);
+
+		/* Leave link local address management to the kernel */
+		if (IN6_IS_ADDR_LINKLOCAL (&address->address))
+			continue;
+
+		if (!array_contains_ip6_address (known_addresses, address))
+			nm_platform_ip6_address_delete (ifindex, address->address, address->plen);
+	}
+	g_array_free (addresses, TRUE);
+
+	if (!known_addresses)
+		return TRUE;
+
+	/* Add missing addresses */
+	for (i = 0; i < known_addresses->len; i++) {
+		const NMPlatformIP6Address *known_address = &g_array_index (known_addresses, NMPlatformIP6Address, i);
+		guint32 lifetime, preferred;
+
+		/* add a padding of 5 seconds to avoid potential races. */
+		if (!_address_get_lifetime ((NMPlatformIPAddress *) known_address, now, 5, &lifetime, &preferred))
+			continue;
+
+		if (!nm_platform_ip6_address_add (ifindex, known_address->address,
+		                                  known_address->peer_address, known_address->plen,
+		                                  lifetime, preferred, known_address->flags))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_platform_address_flush (int ifindex)
+{
+	return nm_platform_ip4_address_sync (ifindex, NULL)
+			&& nm_platform_ip6_address_sync (ifindex, NULL);
+}
+
+/******************************************************************/
+
+GArray *
+nm_platform_ip4_route_get_all (int ifindex, gboolean include_default)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, NULL);
+	g_return_val_if_fail (klass->ip4_route_get_all, NULL);
+
+	return klass->ip4_route_get_all (platform, ifindex, include_default);
+}
+
+GArray *
+nm_platform_ip6_route_get_all (int ifindex, gboolean include_default)
+{
+	reset_error ();
+
+	g_return_val_if_fail (ifindex > 0, NULL);
+	g_return_val_if_fail (klass->ip6_route_get_all, NULL);
+
+	return klass->ip6_route_get_all (platform, ifindex, include_default);
+}
+
+gboolean
+nm_platform_ip4_route_add (int ifindex, NMPlatformSource source,
+                           in_addr_t network, int plen,
+                           in_addr_t gateway, int metric, int mss)
+{
+	reset_error ();
+
+	g_return_val_if_fail (platform, FALSE);
+	g_return_val_if_fail (0 <= plen && plen <= 32, FALSE);
+	g_return_val_if_fail (metric >= 0, FALSE);
+	g_return_val_if_fail (mss >= 0, FALSE);
+	g_return_val_if_fail (klass->ip4_route_add, FALSE);
+
+	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+		NMPlatformIP4Route route = { 0 };
+
+		route.ifindex = ifindex;
+		route.source = source;
+		route.network = network;
+		route.plen = plen;
+		route.gateway = gateway;
+		route.metric = metric;
+		route.mss = mss;
+
+		debug ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route));
+	}
+	return klass->ip4_route_add (platform, ifindex, source, network, plen, gateway, metric, mss);
+}
+
+gboolean
+nm_platform_ip6_route_add (int ifindex, NMPlatformSource source,
+                           struct in6_addr network, int plen, struct in6_addr gateway,
+                           int metric, int mss)
+{
+	g_return_val_if_fail (platform, FALSE);
+	g_return_val_if_fail (0 <= plen && plen <= 128, FALSE);
+	g_return_val_if_fail (metric >= 0, FALSE);
+	g_return_val_if_fail (mss >= 0, FALSE);
+	g_return_val_if_fail (klass->ip6_route_add, FALSE);
+
+	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+		NMPlatformIP6Route route = { 0 };
+
+		route.ifindex = ifindex;
+		route.source = source;
+		route.network = network;
+		route.plen = plen;
+		route.gateway = gateway;
+		route.metric = metric;
+		route.mss = mss;
+
+		debug ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route));
+	}
+	return klass->ip6_route_add (platform, ifindex, source, network, plen, gateway, metric, mss);
+}
+
+gboolean
+nm_platform_ip4_route_delete (int ifindex, in_addr_t network, int plen, int metric)
+{
+	reset_error ();
+
+	g_return_val_if_fail (platform, FALSE);
+	g_return_val_if_fail (klass->ip4_route_delete, FALSE);
+
+	debug ("route: deleting IPv4 route %s/%d, metric=%d", nm_utils_inet4_ntop (network, NULL), plen, metric);
+	return klass->ip4_route_delete (platform, ifindex, network, plen, metric);
+}
+
+gboolean
+nm_platform_ip6_route_delete (int ifindex, struct in6_addr network, int plen, int metric)
+{
+	reset_error ();
+
+	g_return_val_if_fail (platform, FALSE);
+	g_return_val_if_fail (klass->ip6_route_delete, FALSE);
+
+	debug ("route: deleting IPv6 route %s/%d, metric=%d", nm_utils_inet6_ntop (&network, NULL), plen, metric);
+	return klass->ip6_route_delete (platform, ifindex, network, plen, metric);
+}
+
+gboolean
+nm_platform_ip4_route_exists (int ifindex, in_addr_t network, int plen, int metric)
+{
+	reset_error ();
+
+	g_return_val_if_fail (platform, FALSE);
+	g_return_val_if_fail (klass->ip4_route_exists, FALSE);
+
+	return klass->ip4_route_exists (platform,ifindex, network, plen, metric);
+}
+
+gboolean
+nm_platform_ip6_route_exists (int ifindex, struct in6_addr network, int plen, int metric)
+{
+	reset_error ();
+
+	g_return_val_if_fail (platform, FALSE);
+	g_return_val_if_fail (klass->ip6_route_exists, FALSE);
+
+	return klass->ip6_route_exists (platform, ifindex, network, plen, metric);
+}
+
+static gboolean
+array_contains_ip4_route (const GArray *routes, const NMPlatformIP4Route *route)
+{
+	guint len = routes ? routes->len : 0;
+	guint i;
+
+	for (i = 0; i < len; i++) {
+		NMPlatformIP4Route *c = &g_array_index (routes, NMPlatformIP4Route, i);
+
+		if (route->network == c->network &&
+		    route->plen == c->plen &&
+		    route->gateway == c->gateway &&
+		    route->metric == c->metric)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+array_contains_ip6_route (const GArray *routes, const NMPlatformIP6Route *route)
+{
+	guint len = routes ? routes->len : 0;
+	guint i;
+
+	for (i = 0; i < len; i++) {
+		NMPlatformIP6Route *c = &g_array_index (routes, NMPlatformIP6Route, i);
+
+		if (IN6_ARE_ADDR_EQUAL (&route->network, &c->network) &&
+		    route->plen == c->plen &&
+		    IN6_ARE_ADDR_EQUAL (&route->gateway, &c->gateway) &&
+		    route->metric == c->metric)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+/**
+ * nm_platform_ip4_route_sync:
+ * @ifindex: Interface index
+ * @known_routes: List of routes
+ *
+ * A convenience function to synchronize routes for a specific interface
+ * with the least possible disturbance. It simply removes routes that are
+ * not listed and adds routes that are.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+nm_platform_ip4_route_sync (int ifindex, const GArray *known_routes)
+{
+	GArray *routes;
+	NMPlatformIP4Route *route;
+	const NMPlatformIP4Route *known_route;
+	gboolean success;
+	int i, i_type;
+
+	/* Delete unknown routes */
+	routes = nm_platform_ip4_route_get_all (ifindex, FALSE);
+	for (i = 0; i < routes->len; i++) {
+		route = &g_array_index (routes, NMPlatformIP4Route, i);
+
+		if (!array_contains_ip4_route (known_routes, route))
+			nm_platform_ip4_route_delete (ifindex, route->network, route->plen, route->metric);
+	}
+
+	if (!known_routes) {
+		g_array_free (routes, TRUE);
+		return TRUE;
+	}
+
+	/* Add missing routes */
+	for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) {
+		for (i = 0; i < known_routes->len && success; i++) {
+			known_route = &g_array_index (known_routes, NMPlatformIP4Route, i);
+
+			if ((known_route->gateway == 0) ^ (i_type != 0)) {
+				/* Make two runs over the list of routes. On the first, only add
+				 * device routes, on the second the others (gateway routes). */
+				continue;
+			}
+
+			/* Ignore routes that already exist */
+			if (!array_contains_ip4_route (routes, known_route)) {
+				success = nm_platform_ip4_route_add (ifindex,
+				                                     known_route->source,
+				                                     known_route->network,
+				                                     known_route->plen,
+				                                     known_route->gateway,
+				                                     known_route->metric,
+				                                     known_route->mss);
+				if (!success && known_route->source < NM_PLATFORM_SOURCE_USER) {
+					nm_log_dbg (LOGD_PLATFORM, "ignore error adding IPv4 route to kernel: %s",
+					                           nm_platform_ip4_route_to_string (known_route));
+					success = TRUE;
+				}
+			}
+		}
+	}
+
+	g_array_free (routes, TRUE);
+	return success;
+}
+
+/**
+ * nm_platform_ip6_route_sync:
+ * @ifindex: Interface index
+ * @known_routes: List of routes
+ *
+ * A convenience function to synchronize routes for a specific interface
+ * with the least possible disturbance. It simply removes routes that are
+ * not listed and adds routes that are.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes)
+{
+	GArray *routes;
+	NMPlatformIP6Route *route;
+	const NMPlatformIP6Route *known_route;
+	gboolean success;
+	int i, i_type;
+
+	/* Delete unknown routes */
+	routes = nm_platform_ip6_route_get_all (ifindex, FALSE);
+	for (i = 0; i < routes->len; i++) {
+		route = &g_array_index (routes, NMPlatformIP6Route, i);
+		route->ifindex = 0;
+
+		if (!array_contains_ip6_route (known_routes, route))
+			nm_platform_ip6_route_delete (ifindex, route->network, route->plen, route->metric);
+	}
+
+	if (!known_routes) {
+		g_array_free (routes, TRUE);
+		return TRUE;
+	}
+
+	/* Add missing routes */
+	for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) {
+		for (i = 0; i < known_routes->len && success; i++) {
+			known_route = &g_array_index (known_routes, NMPlatformIP6Route, i);
+
+			if (IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway) ^ (i_type != 0)) {
+				/* Make two runs over the list of routes. On the first, only add
+				 * device routes, on the second the others (gateway routes). */
+				continue;
+			}
+
+			/* Ignore routes that already exist */
+			if (!array_contains_ip6_route (routes, known_route)) {
+				success = nm_platform_ip6_route_add (ifindex,
+				                                     known_route->source,
+				                                     known_route->network,
+				                                     known_route->plen,
+				                                     known_route->gateway,
+				                                     known_route->metric,
+				                                     known_route->mss);
+				if (!success && known_route->source < NM_PLATFORM_SOURCE_USER) {
+					nm_log_dbg (LOGD_PLATFORM, "ignore error adding IPv6 route to kernel: %s",
+					                           nm_platform_ip6_route_to_string (known_route));
+					success = TRUE;
+				}
+			}
+		}
+	}
+
+	g_array_free (routes, TRUE);
+	return success;
+}
+
+gboolean
+nm_platform_route_flush (int ifindex)
+{
+	return nm_platform_ip4_route_sync (ifindex, NULL)
+			&& nm_platform_ip6_route_sync (ifindex, NULL);
+}
+
+/******************************************************************/
+
+static const char *
+source_to_string (NMPlatformSource source)
+{
+	switch (source) {
+	case NM_PLATFORM_SOURCE_KERNEL:
+		return "kernel";
+	case NM_PLATFORM_SOURCE_SHARED:
+		return "shared";
+	case NM_PLATFORM_SOURCE_IP4LL:
+		return "ipv4ll";
+	case NM_PLATFORM_SOURCE_PPP:
+		return "ppp";
+	case NM_PLATFORM_SOURCE_WWAN:
+		return "wwan";
+	case NM_PLATFORM_SOURCE_VPN:
+		return "vpn";
+	case NM_PLATFORM_SOURCE_DHCP:
+		return "dhcp";
+	case NM_PLATFORM_SOURCE_RDISC:
+		return "rdisc";
+	case NM_PLATFORM_SOURCE_USER:
+		return "user";
+	default:
+		break;
+	}
+	return "unknown";
+}
+
+#define TO_STRING_DEV_BUF_SIZE (5+15+1)
+static void
+_to_string_dev (int ifindex, char *buf, size_t size)
+{
+	g_assert (buf && size >= TO_STRING_DEV_BUF_SIZE);
+
+	if (ifindex){
+		const char *name = ifindex > 0 ? nm_platform_link_get_name (ifindex) : NULL;
+
+		strcpy (buf, " dev ");
+		buf += 5;
+		size -= 5;
+
+		if (name)
+			g_strlcpy (buf, name, size);
+		else
+			g_snprintf (buf, size, "%d", ifindex);
+	} else
+		buf[0] = 0;
+}
+
+static const char *
+_lifetime_to_string (guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size)
+{
+	if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT)
+		return "forever";
+
+	g_snprintf (buf, buf_size, "%usec",
+	            _rebase_relative_time_on_now (timestamp, lifetime, now, 0));
+	return buf;
+}
+
+
+static const char *
+_lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, guint32 lifetime, char *buf, size_t buf_size)
+{
+	g_snprintf (buf, buf_size, " lifetime %d-%u[%u,%u]",
+	            (signed) now, (unsigned) timestamp, (unsigned) preferred, (unsigned) lifetime);
+	return buf;
+}
+
+static char to_string_buffer[256];
+
+const char *
+nm_platform_link_to_string (const NMPlatformLink *link)
+{
+	char master[20];
+	char parent[20];
+	char *driver, *udi, *type;
+	GString *str;
+
+	if (!link)
+		return "(unknown link)";
+
+	str = g_string_new (NULL);
+	if (!link->arp)
+		g_string_append (str, "NOARP,");
+	if (link->up)
+		g_string_append (str, "UP");
+	else
+		g_string_append (str, "DOWN");
+	if (link->connected)
+		g_string_append (str, ",LOWER_UP");
+
+	if (link->master)
+		g_snprintf (master, sizeof (master), " master %d", link->master);
+	else
+		master[0] = 0;
+
+	if (link->parent)
+		g_snprintf (parent, sizeof (master), "@%d", link->parent);
+	else
+		parent[0] = 0;
+
+	driver = link->driver ? g_strdup_printf (" driver '%s'", link->driver) : NULL;
+	udi = link->udi ? g_strdup_printf (" udi '%s'", link->udi) : NULL;
+	type = link->type_name ? NULL : g_strdup_printf ("(%d)", link->type);
+
+	g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%d: %s%s <%s> mtu %d%s %s%s%s",
+	            link->ifindex, link->name, parent, str->str,
+	            link->mtu, master, link->type_name ? link->type_name : type,
+	            driver ? driver : "", udi ? udi : "");
+	g_string_free (str, TRUE);
+	g_free (driver);
+	g_free (udi);
+	g_free (type);
+	return to_string_buffer;
+}
+
+/**
+ * nm_platform_ip4_address_to_string:
+ * @route: pointer to NMPlatformIP4Address address structure
+ *
+ * A method for converting an address struct into a string representation.
+ *
+ * Example output: ""
+ *
+ * Returns: a string representation of the address. The returned string
+ * is an internal buffer, so do not keep or free the returned string.
+ * Also, this function is not thread safe.
+ */
+const char *
+nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address)
+{
+	char s_address[INET_ADDRSTRLEN];
+	char s_peer[INET_ADDRSTRLEN];
+	char str_dev[TO_STRING_DEV_BUF_SIZE];
+	char str_label[32];
+	char str_lft[30], str_pref[30], str_time[50];
+	char *str_peer = NULL;
+	const char *str_lft_p, *str_pref_p, *str_time_p;
+	gint32 now = nm_utils_get_monotonic_timestamp_s ();
+
+	g_return_val_if_fail (address, "(unknown)");
+
+	inet_ntop (AF_INET, &address->address, s_address, sizeof (s_address));
+
+	if (address->peer_address) {
+		inet_ntop (AF_INET, &address->peer_address, s_peer, sizeof (s_peer));
+		str_peer = g_strconcat (" ptp ", s_peer, NULL);
+	}
+
+	_to_string_dev (address->ifindex, str_dev, sizeof (str_dev));
+
+	if (*address->label)
+		g_snprintf (str_label, sizeof (str_label), " label %s", address->label);
+	else
+		str_label[0] = 0;
+
+	str_lft_p = _lifetime_to_string (address->timestamp,
+	                                 address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT,
+	                                 now, str_lft, sizeof (str_lft)),
+	str_pref_p = (address->lifetime == address->preferred)
+	             ? str_lft_p
+	             : ( _lifetime_to_string (address->timestamp,
+	                                      address->lifetime ? MIN (address->preferred, address->lifetime) : NM_PLATFORM_LIFETIME_PERMANENT,
+	                                      now, str_pref, sizeof (str_pref)) );
+	str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time));
+
+	g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s",
+	            s_address, address->plen, str_lft_p, str_pref_p, str_time_p,
+	            str_peer ? str_peer : "",
+	            str_dev,
+	            str_label,
+	            source_to_string (address->source));
+	g_free (str_peer);
+	return to_string_buffer;
+}
+
+/**
+ * nm_platform_addr_flags2str: wrapper for rtnl_addr_flags2str(),
+ * which might not yet support some recent address flags.
+ **/
+void
+nm_platform_addr_flags2str (int flags, char *buf, size_t size)
+{
+	rtnl_addr_flags2str(flags, buf, size);
+
+	/* There are two recent flags IFA_F_MANAGETEMPADDR and IFA_F_NOPREFIXROUTE.
+	 * If libnl does not yet support them, add them by hand.
+	 * These two flags were introduced together with the extended ifa_flags,
+	 * so, check for that.
+	 */
+	if ((flags & IFA_F_MANAGETEMPADDR) && !nm_platform_check_support_libnl_extended_ifa_flags ()) {
+		strncat (buf, buf[0] ? "," IFA_F_MANAGETEMPADDR_STR : IFA_F_MANAGETEMPADDR_STR,
+		         size - strlen (buf) - 1);
+	}
+	if ((flags & IFA_F_NOPREFIXROUTE) && !nm_platform_check_support_libnl_extended_ifa_flags ()) {
+		strncat (buf, buf[0] ? "," IFA_F_NOPREFIXROUTE_STR : IFA_F_NOPREFIXROUTE_STR,
+		         size - strlen (buf) - 1);
+	}
+}
+
+/**
+ * nm_platform_ip6_address_to_string:
+ * @route: pointer to NMPlatformIP6Address address structure
+ *
+ * A method for converting an address struct into a string representation.
+ *
+ * Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1"
+ *
+ * Returns: a string representation of the address. The returned string
+ * is an internal buffer, so do not keep or free the returned string.
+ * Also, this function is not thread safe.
+ */
+const char *
+nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address)
+{
+	char s_flags[256];
+	char s_address[INET6_ADDRSTRLEN];
+	char s_peer[INET6_ADDRSTRLEN];
+	char str_lft[30], str_pref[30], str_time[50];
+	char str_dev[TO_STRING_DEV_BUF_SIZE];
+	char *str_flags;
+	char *str_peer = NULL;
+	const char *str_lft_p, *str_pref_p, *str_time_p;
+	gint32 now = nm_utils_get_monotonic_timestamp_s ();
+
+	g_return_val_if_fail (address, "(unknown)");
+
+	inet_ntop (AF_INET6, &address->address, s_address, sizeof (s_address));
+
+	if (!IN6_IS_ADDR_UNSPECIFIED (&address->peer_address)) {
+		inet_ntop (AF_INET6, &address->peer_address, s_peer, sizeof (s_peer));
+		str_peer = g_strconcat (" ptp ", s_peer, NULL);
+	}
+
+	_to_string_dev (address->ifindex, str_dev, sizeof (str_dev));
+
+	nm_platform_addr_flags2str (address->flags, s_flags, sizeof (s_flags));
+
+	str_flags = s_flags[0] ? g_strconcat (" flags ", s_flags, NULL) : NULL;
+
+	str_lft_p = _lifetime_to_string (address->timestamp,
+	                                 address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT,
+	                                 now, str_lft, sizeof (str_lft)),
+	str_pref_p = (address->lifetime == address->preferred)
+	             ? str_lft_p
+	             : ( _lifetime_to_string (address->timestamp,
+	                                      address->lifetime ? MIN (address->preferred, address->lifetime) : NM_PLATFORM_LIFETIME_PERMANENT,
+	                                      now, str_pref, sizeof (str_pref)) );
+	str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time));
+
+	g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s",
+	            s_address, address->plen, str_lft_p, str_pref_p, str_time_p,
+	            str_peer ? str_peer : "",
+	            str_dev,
+	            str_flags ? str_flags : "",
+	            source_to_string (address->source));
+	g_free (str_flags);
+	g_free (str_peer);
+	return to_string_buffer;
+}
+
+/**
+ * nm_platform_ip4_route_to_string:
+ * @route: pointer to NMPlatformIP4Route route structure
+ *
+ * A method for converting a route struct into a string representation.
+ *
+ * Example output: "192.168.1.0/24 via 0.0.0.0 dev em1 metric 0 mss 0"
+ *
+ * Returns: a string representation of the route. The returned string
+ * is an internal buffer, so do not keep or free the returned string.
+ * Also, this function is not thread safe.
+ */
+const char *
+nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route)
+{
+	char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN];
+	char str_dev[TO_STRING_DEV_BUF_SIZE];
+
+	g_return_val_if_fail (route, "(unknown)");
+
+	inet_ntop (AF_INET, &route->network, s_network, sizeof(s_network));
+	inet_ntop (AF_INET, &route->gateway, s_gateway, sizeof(s_gateway));
+
+	_to_string_dev (route->ifindex, str_dev, sizeof (str_dev));
+
+	g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %u mss %u src %s",
+	            s_network, route->plen, s_gateway,
+	            str_dev,
+	            route->metric, route->mss,
+	            source_to_string (route->source));
+	return to_string_buffer;
+}
+
+/**
+ * nm_platform_ip6_route_to_string:
+ * @route: pointer to NMPlatformIP6Route route structure
+ *
+ * A method for converting a route struct into a string representation.
+ *
+ * Example output: "ff02::fb/128 via :: dev em1 metric 0"
+ *
+ * Returns: a string representation of the route. The returned string
+ * is an internal buffer, so do not keep or free the returned string.
+ * Also, this function is not thread safe.
+ */
+const char *
+nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route)
+{
+	char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN];
+	char str_dev[TO_STRING_DEV_BUF_SIZE];
+
+	g_return_val_if_fail (route, "(unknown)");
+
+	inet_ntop (AF_INET6, &route->network, s_network, sizeof(s_network));
+	inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway));
+
+	_to_string_dev (route->ifindex, str_dev, sizeof (str_dev));
+
+	g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %u mss %u src %s",
+	            s_network, route->plen, s_gateway,
+	            str_dev,
+	            route->metric, route->mss,
+	            source_to_string (route->source));
+	return to_string_buffer;
+}
+
+#define _CMP_POINTER(a, b)                                  \
+    G_STMT_START {                                          \
+        if ((a) == (b))                                     \
+            return 0;                                       \
+        if (!(a))                                           \
+            return -1;                                      \
+        if (!(b))                                           \
+            return 1;                                       \
+    } G_STMT_END
+
+#define _CMP_FIELD(a, b, field)                             \
+    G_STMT_START {                                          \
+        if (((a)->field) != ((b)->field))                   \
+            return (((a)->field) < ((b)->field)) ? -1 : 1;  \
+    } G_STMT_END
+
+#define _CMP_FIELD_STR(a, b, field)                         \
+    G_STMT_START {                                          \
+        int c = strcmp ((a)->field, (b)->field);            \
+        if (c != 0)                                         \
+            return c < 0 ? -1 : 1;                          \
+    } G_STMT_END
+
+#define _CMP_FIELD_STR0(a, b, field)                        \
+    G_STMT_START {                                          \
+        int c = g_strcmp0 ((a)->field, (b)->field);         \
+        if (c != 0)                                         \
+            return c < 0 ? -1 : 1;                          \
+    } G_STMT_END
+
+#define _CMP_FIELD_MEMCMP(a, b, field)                      \
+    G_STMT_START {                                          \
+        int c = memcmp (&((a)->field), &((b)->field),       \
+                        sizeof ((a)->field));               \
+        if (c != 0)                                         \
+            return c < 0 ? -1 : 1;                          \
+    } G_STMT_END
+
+int
+nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b)
+{
+	_CMP_POINTER (a, b);
+	_CMP_FIELD (a, b, type);
+	_CMP_FIELD_STR (a, b, name);
+	_CMP_FIELD (a, b, master);
+	_CMP_FIELD (a, b, parent);
+	_CMP_FIELD (a, b, up);
+	_CMP_FIELD (a, b, connected);
+	_CMP_FIELD (a, b, arp);
+	_CMP_FIELD (a, b, mtu);
+	_CMP_FIELD_STR0 (a, b, type_name);
+	_CMP_FIELD_STR0 (a, b, udi);
+	_CMP_FIELD_STR0 (a, b, driver);
+	return 0;
+}
+
+int
+nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b)
+{
+	_CMP_POINTER (a, b);
+	_CMP_FIELD (a, b, ifindex);
+	_CMP_FIELD (a, b, source);
+	_CMP_FIELD (a, b, address);
+	_CMP_FIELD (a, b, peer_address);
+	_CMP_FIELD (a, b, plen);
+	_CMP_FIELD (a, b, timestamp);
+	_CMP_FIELD (a, b, lifetime);
+	_CMP_FIELD (a, b, preferred);
+	_CMP_FIELD_STR (a, b, label);
+	return 0;
+}
+
+int
+nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
+{
+	_CMP_POINTER (a, b);
+	_CMP_FIELD (a, b, ifindex);
+	_CMP_FIELD (a, b, source);
+	_CMP_FIELD_MEMCMP (a, b, address);
+	_CMP_FIELD_MEMCMP (a, b, peer_address);
+	_CMP_FIELD (a, b, plen);
+	_CMP_FIELD (a, b, timestamp);
+	_CMP_FIELD (a, b, lifetime);
+	_CMP_FIELD (a, b, preferred);
+	_CMP_FIELD (a, b, flags);
+	return 0;
+}
+
+int
+nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b)
+{
+	_CMP_POINTER (a, b);
+	_CMP_FIELD (a, b, ifindex);
+	_CMP_FIELD (a, b, source);
+	_CMP_FIELD (a, b, network);
+	_CMP_FIELD (a, b, plen);
+	_CMP_FIELD (a, b, gateway);
+	_CMP_FIELD (a, b, metric);
+	_CMP_FIELD (a, b, mss);
+	return 0;
+}
+
+int
+nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b)
+{
+	_CMP_POINTER (a, b);
+	_CMP_FIELD (a, b, ifindex);
+	_CMP_FIELD (a, b, source);
+	_CMP_FIELD_MEMCMP (a, b, network);
+	_CMP_FIELD (a, b, plen);
+	_CMP_FIELD_MEMCMP (a, b, gateway);
+	_CMP_FIELD (a, b, metric);
+	_CMP_FIELD (a, b, mss);
+	return 0;
+}
+
+#undef _CMP_FIELD
+#undef _CMP_FIELD_MEMCMP
+
+/**
+ * nm_platform_ip_address_cmp_expiry:
+ * @a: a NMPlatformIPAddress to compare
+ * @b: the other NMPlatformIPAddress to compare
+ *
+ * Compares two addresses and returns which one has a longer remaining lifetime.
+ * If both addresses have the same lifetime, look at the remaining preferred time.
+ *
+ * For comparison, only the timestamp, lifetime and preferred fields are considered.
+ * If they compare equal (== 0), their other fields were not considered.
+ *
+ * Returns: -1, 0, or 1 according to the comparison
+ **/
+int
+nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatformIPAddress *b)
+{
+	gint64 ta = 0, tb = 0;
+
+	_CMP_POINTER (a, b);
+
+	if (a->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0)
+		ta = G_MAXINT64;
+	else if (a->timestamp)
+		ta = ((gint64) a->timestamp) + a->lifetime;
+
+	if (b->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || b->lifetime == 0)
+		tb = G_MAXINT64;
+	else if (b->timestamp)
+		tb = ((gint64) b->timestamp) + b->lifetime;
+
+	if (ta == tb) {
+		/* if the lifetime is equal, compare the preferred time. */
+		ta = tb = 0;
+
+		if (a->preferred == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0 /* liftime==0 means permanent! */)
+			ta = G_MAXINT64;
+		else if (a->timestamp)
+			ta = ((gint64) a->timestamp) + a->preferred;
+
+		if (b->preferred == NM_PLATFORM_LIFETIME_PERMANENT|| b->lifetime == 0)
+			tb = G_MAXINT64;
+		else if (b->timestamp)
+			tb = ((gint64) b->timestamp) + b->preferred;
+
+		if (ta == tb)
+			return 0;
+	}
+
+	return ta < tb ? -1 : 1;
+}
+
+#undef _CMP_POINTER
+
+static const char *
+_change_type_to_string (NMPlatformSignalChangeType change_type)
+{
+	switch (change_type) {
+	case NM_PLATFORM_SIGNAL_ADDED:
+		return "added";
+	case NM_PLATFORM_SIGNAL_CHANGED:
+		return "changed";
+	case NM_PLATFORM_SIGNAL_REMOVED:
+		return "removed";
+	default:
+		g_return_val_if_reached ("UNKNOWN");
+	}
+}
+
+static void
+log_link (NMPlatform *p, int ifindex, NMPlatformLink *device, NMPlatformSignalChangeType change_type, gpointer user_data)
+{
+
+	debug ("signal: link %7s: %s", _change_type_to_string (change_type), nm_platform_link_to_string (device));
+}
+
+static void
+log_ip4_address (NMPlatform *p, int ifindex, NMPlatformIP4Address *address, NMPlatformSignalChangeType change_type, gpointer user_data)
+{
+	debug ("signal: address 4 %7s: %s", _change_type_to_string (change_type), nm_platform_ip4_address_to_string (address));
+}
+
+static void
+log_ip6_address (NMPlatform *p, int ifindex, NMPlatformIP6Address *address, NMPlatformSignalChangeType change_type, gpointer user_data)
+{
+	debug ("signal: address 6 %7s: %s", _change_type_to_string (change_type), nm_platform_ip6_address_to_string (address));
+}
+
+static void
+log_ip4_route (NMPlatform *p, int ifindex, NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gpointer user_data)
+{
+	debug ("signal: route   4 %7s: %s", _change_type_to_string (change_type), nm_platform_ip4_route_to_string (route));
+}
+
+static void
+log_ip6_route (NMPlatform *p, int ifindex, NMPlatformIP6Route *route, NMPlatformSignalChangeType change_type, gpointer user_data)
+{
+	debug ("signal: route   6 %7s: %s", _change_type_to_string (change_type), nm_platform_ip6_route_to_string (route));
+}
+
+/******************************************************************/
+
+static void
+nm_platform_init (NMPlatform *object)
+{
+}
+
+#define SIGNAL(signal_id, method) signals[signal_id] = \
+	g_signal_new_class_handler (NM_PLATFORM_ ## signal_id, \
+		G_OBJECT_CLASS_TYPE (object_class), \
+		G_SIGNAL_RUN_FIRST, \
+		G_CALLBACK (method), \
+		NULL, NULL, NULL, \
+		G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_POINTER, NM_TYPE_PLATFORM_SIGNAL_CHANGE_TYPE, NM_TYPE_PLATFORM_REASON);
+
+static void
+nm_platform_class_init (NMPlatformClass *platform_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (platform_class);
+
+	/* Signals */
+	SIGNAL (SIGNAL_LINK_CHANGED, log_link)
+	SIGNAL (SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address)
+	SIGNAL (SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address)
+	SIGNAL (SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route)
+	SIGNAL (SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route)
+}
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
new file mode 100644
index 00000000..96b6cfab
--- /dev/null
+++ b/src/platform/nm-platform.h
@@ -0,0 +1,634 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-platform.c - Handle runtime kernel networking configuration
+ *
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_PLATFORM_H
+#define NM_PLATFORM_H
+
+#include <glib-object.h>
+#include "nm-glib-compat.h"
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#include <linux/if.h>
+#include <linux/if_addr.h>
+
+#include <NetworkManager.h>
+
+#define NM_TYPE_PLATFORM            (nm_platform_get_type ())
+#define NM_PLATFORM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform))
+#define NM_PLATFORM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_PLATFORM, NMPlatformClass))
+#define NM_IS_PLATFORM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_PLATFORM))
+#define NM_IS_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_PLATFORM))
+#define NM_PLATFORM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_PLATFORM, NMPlatformClass))
+
+/******************************************************************/
+
+/* workaround for older libnl version, that does not define these flags. */
+#ifndef IFA_F_MANAGETEMPADDR
+#define IFA_F_MANAGETEMPADDR 0x100
+#endif
+#ifndef IFA_F_NOPREFIXROUTE
+#define IFA_F_NOPREFIXROUTE 0x200
+#endif
+
+typedef enum {
+	/* no error specified, sometimes this means the arguments were wrong */
+	NM_PLATFORM_ERROR_NONE,
+	/* object was not found */
+	NM_PLATFORM_ERROR_NOT_FOUND,
+	/* object already exists */
+	NM_PLATFORM_ERROR_EXISTS,
+	/* object is wrong type */
+	NM_PLATFORM_ERROR_WRONG_TYPE,
+	/* object is not a slave */
+	NM_PLATFORM_ERROR_NOT_SLAVE,
+	/* firmware is not found */
+	NM_PLATFORM_ERROR_NO_FIRMWARE
+} NMPlatformError;
+
+typedef enum {
+	NM_PLATFORM_REASON_NONE,
+	/* Event was requested by NetworkManager. */
+	NM_PLATFORM_REASON_INTERNAL,
+	/* Event came from the kernel. */
+	NM_PLATFORM_REASON_EXTERNAL,
+	/* Event is a result of cache checking and cleanups. */
+	NM_PLATFORM_REASON_CACHE_CHECK
+} NMPlatformReason;
+
+typedef enum {
+	/* Please don't interpret type numbers outside nm-platform and use functions
+	 * like nm_platform_link_is_software() and nm_platform_supports_slaves().
+	 *
+	 * type & 0x10000 -> Software device type
+	 * type & 0x20000 -> Type supports slaves
+	 */
+
+	/* No type, used as error value */
+	NM_LINK_TYPE_NONE,
+
+	/* Unknown type  */
+	NM_LINK_TYPE_UNKNOWN,
+
+	/* Hardware types */
+	NM_LINK_TYPE_ETHERNET,
+	NM_LINK_TYPE_INFINIBAND,
+	NM_LINK_TYPE_OLPC_MESH,
+	NM_LINK_TYPE_WIFI,
+	NM_LINK_TYPE_WWAN_ETHERNET,   /* WWAN pseudo-ethernet */
+	NM_LINK_TYPE_WIMAX,
+
+	/* Software types */
+	NM_LINK_TYPE_DUMMY = 0x10000,
+	NM_LINK_TYPE_GRE,
+	NM_LINK_TYPE_GRETAP,
+	NM_LINK_TYPE_IFB,
+	NM_LINK_TYPE_LOOPBACK,
+	NM_LINK_TYPE_MACVLAN,
+	NM_LINK_TYPE_MACVTAP,
+	NM_LINK_TYPE_OPENVSWITCH,
+	NM_LINK_TYPE_TAP,
+	NM_LINK_TYPE_TUN,
+	NM_LINK_TYPE_VETH,
+	NM_LINK_TYPE_VLAN,
+	NM_LINK_TYPE_VXLAN,
+
+	/* Software types with slaves */
+	NM_LINK_TYPE_BRIDGE = 0x10000 | 0x20000,
+	NM_LINK_TYPE_BOND,
+	NM_LINK_TYPE_TEAM,
+} NMLinkType;
+
+#define __NMPlatformObject_COMMON \
+	int ifindex; \
+	;
+
+typedef struct {
+	__NMPlatformObject_COMMON;
+	char name[IFNAMSIZ];
+	NMLinkType type;
+	const char *type_name;
+	const char *udi;
+	const char *driver;
+	int master;
+	int parent;
+	gboolean up;
+	gboolean connected;
+	gboolean arp;
+	guint mtu;
+} NMPlatformLink;
+
+typedef enum {
+	NM_PLATFORM_SIGNAL_ADDED,
+	NM_PLATFORM_SIGNAL_CHANGED,
+	NM_PLATFORM_SIGNAL_REMOVED,
+} NMPlatformSignalChangeType;
+
+#define NM_PLATFORM_LIFETIME_PERMANENT G_MAXUINT32
+
+typedef enum {
+	/* In priority order; higher number == higher priority */
+	NM_PLATFORM_SOURCE_UNKNOWN,
+	NM_PLATFORM_SOURCE_KERNEL,
+	NM_PLATFORM_SOURCE_SHARED,
+	NM_PLATFORM_SOURCE_IP4LL,
+	NM_PLATFORM_SOURCE_PPP,
+	NM_PLATFORM_SOURCE_WWAN,
+	NM_PLATFORM_SOURCE_VPN,
+	NM_PLATFORM_SOURCE_DHCP,
+	NM_PLATFORM_SOURCE_RDISC,
+	NM_PLATFORM_SOURCE_USER,
+} NMPlatformSource;
+
+
+typedef struct {
+	__NMPlatformObject_COMMON;
+} NMPlatformObject;
+
+
+#define __NMPlatformIPAddress_COMMON \
+	__NMPlatformObject_COMMON; \
+	NMPlatformSource source; \
+	\
+	/* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*().
+	 *
+	 * The rules are:
+	 * 1 @lifetime==0: @timestamp and @preferred is irrelevant (but mostly set to 0 too). Such addresses
+	 *   are permanent. This rule is so that unset addresses (calloc) are permanent by default.
+	 * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly
+	 *   set to 0). Such addresses are permanent.
+	 * 3 Non permanent addreses should (almost) always have @timestamp > 0. 0 is not a valid timestamp
+	 *   and never returned by nm_utils_get_monotonic_timestamp_s(). In this case @valid/@preferred
+	 *   is anchored at @timestamp.
+	 * 4 Non permanent addresses with @timestamp == 0 are implicitely anchored at *now*, thus the time
+	 *   moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add().
+	 *
+	 * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the
+	 * lease was received. Addresses from kernel might have the @timestamp based on the last modification
+	 * time of the addresses. But don't rely on this behaviour, the @timestamp is only defined for anchoring
+	 * @lifetime and @preferred.
+	 */ \
+	guint32 timestamp; \
+	guint32 lifetime;   /* seconds since timestamp */ \
+	guint32 preferred;  /* seconds since timestamp */ \
+	int plen; \
+	;
+
+/**
+ * NMPlatformIPAddress:
+ *
+ * Common parts of NMPlatformIP4Address and NMPlatformIP6Address.
+ **/
+typedef struct {
+	__NMPlatformIPAddress_COMMON;
+	union {
+		guint8 address_ptr[1];
+		guint32 __dummy_for_32bit_alignment;
+	};
+} NMPlatformIPAddress;
+
+/**
+ * NMPlatformIP4Address:
+ * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_s()
+ **/
+typedef struct {
+	__NMPlatformIPAddress_COMMON;
+	in_addr_t address;
+	in_addr_t peer_address;  /* PTP peer address */
+	char label[IFNAMSIZ];
+} NMPlatformIP4Address;
+G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Address, address));
+
+/**
+ * NMPlatformIP6Address:
+ * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_s()
+ **/
+typedef struct {
+	__NMPlatformIPAddress_COMMON;
+	struct in6_addr address;
+	struct in6_addr peer_address;
+	guint flags; /* ifa_flags from <linux/if_addr.h>, field type "unsigned int" is as used in rtnl_addr_get_flags. */
+} NMPlatformIP6Address;
+G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Address, address));
+
+#undef __NMPlatformIPAddress_COMMON
+
+
+#define NM_PLATFORM_ROUTE_METRIC_DEFAULT 1024
+
+#define __NMPlatformIPRoute_COMMON \
+	__NMPlatformObject_COMMON; \
+	NMPlatformSource source; \
+	int plen; \
+	guint metric; \
+	guint mss; \
+	;
+
+typedef struct {
+	__NMPlatformIPRoute_COMMON;
+	union {
+		guint8 network_ptr[1];
+		guint32 __dummy_for_32bit_alignment;
+	};
+} NMPlatformIPRoute;
+
+typedef struct {
+	__NMPlatformIPRoute_COMMON;
+	in_addr_t network;
+	in_addr_t gateway;
+} NMPlatformIP4Route;
+G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network));
+
+typedef struct {
+	__NMPlatformIPRoute_COMMON;
+	struct in6_addr network;
+	struct in6_addr gateway;
+} NMPlatformIP6Route;
+G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Route, network));
+
+#undef __NMPlatformIPRoute_COMMON
+
+
+#undef __NMPlatformObject_COMMON
+
+
+typedef struct {
+	int peer;
+} NMPlatformVethProperties;
+
+typedef struct {
+	gint64 owner;
+	gint64 group;
+	const char *mode;
+	gboolean no_pi;
+	gboolean vnet_hdr;
+	gboolean multi_queue;
+} NMPlatformTunProperties;
+
+typedef struct {
+	int parent_ifindex;
+	const char *mode;
+	gboolean no_promisc;
+} NMPlatformMacvlanProperties;
+
+typedef struct {
+	int parent_ifindex;
+	guint32 id;
+	in_addr_t group;
+	in_addr_t local;
+	struct in6_addr group6;
+	struct in6_addr local6;
+	guint8 tos;
+	guint8 ttl;
+	gboolean learning;
+	guint32 ageing;
+	guint32 limit;
+	guint16 dst_port;
+	guint16 src_port_min;
+	guint16 src_port_max;
+	gboolean proxy;
+	gboolean rsc;
+	gboolean l2miss;
+	gboolean l3miss;
+} NMPlatformVxlanProperties;
+
+typedef struct {
+	int parent_ifindex;
+	guint16 input_flags;
+	guint16 output_flags;
+	guint32 input_key;
+	guint32 output_key;
+	in_addr_t local;
+	in_addr_t remote;
+	guint8 ttl;
+	guint8 tos;
+	gboolean path_mtu_discovery;
+} NMPlatformGreProperties;
+
+/******************************************************************/
+
+/* NMPlatform abstract class and its implementations provide a layer between
+ * networkmanager's device management classes and the operating system kernel.
+ *
+ * How it works, is best seen in tests/nm-platform-test.c source file.
+ *
+ * NMPlatform provides interface to configure kernel interfaces and receive
+ * notifications about both internal and external configuration changes. It
+ * respects the following rules:
+ *
+ * 1) Every change made through NMPlatform is readily available and the respective
+ * signals are called synchronously.
+ *
+ * 2) State of an object retrieved from NMPlatform (through functions or events)
+ * is at least as recent than the state retrieved before.
+ *
+ * Any failure of the above rules should be fixed in NMPlatform implementations
+ * and tested in nm-platform-test. Synchronization hacks should never be put
+ * to any other code. That's why NMPlatform was created and that's why the
+ * testing code was written for it.
+ *
+ * In future, parts of linux platform implementation may be moved to the libnl
+ * library.
+ *
+ * If you have any problems related to NMPlatform on your system, you should
+ * always first run tests/nm-linux-platform-test as root and with all
+ * network configuration daemons stopped. Look at the code first.
+ */
+
+typedef struct {
+	GObject parent;
+
+	NMPlatformError error;
+} NMPlatform;
+
+typedef struct {
+	GObjectClass parent;
+
+	gboolean (*setup) (NMPlatform *);
+
+	gboolean (*sysctl_set) (NMPlatform *, const char *path, const char *value);
+	char * (*sysctl_get) (NMPlatform *, const char *path);
+
+	gboolean (*link_get) (NMPlatform *platform, int ifindex, NMPlatformLink *link);
+	GArray *(*link_get_all) (NMPlatform *);
+	gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, const void *address, size_t address_len);
+	gboolean (*link_delete) (NMPlatform *, int ifindex);
+	int (*link_get_ifindex) (NMPlatform *, const char *name);
+	const char *(*link_get_name) (NMPlatform *, int ifindex);
+	NMLinkType (*link_get_type) (NMPlatform *, int ifindex);
+	const char *(*link_get_type_name) (NMPlatform *, int ifindex);
+
+	gboolean (*link_refresh) (NMPlatform *, int ifindex);
+
+	gboolean (*link_set_up) (NMPlatform *, int ifindex);
+	gboolean (*link_set_down) (NMPlatform *, int ifindex);
+	gboolean (*link_set_arp) (NMPlatform *, int ifindex);
+	gboolean (*link_set_noarp) (NMPlatform *, int ifindex);
+	gboolean (*link_is_up) (NMPlatform *, int ifindex);
+	gboolean (*link_is_connected) (NMPlatform *, int ifindex);
+	gboolean (*link_uses_arp) (NMPlatform *, int ifindex);
+
+	gconstpointer (*link_get_address) (NMPlatform *, int ifindex, size_t *length);
+	gboolean (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length);
+	guint32 (*link_get_mtu) (NMPlatform *, int ifindex);
+	gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu);
+
+	char * (*link_get_physical_port_id) (NMPlatform *, int ifindex);
+	gboolean (*link_get_wake_on_lan) (NMPlatform *, int ifindex);
+
+	gboolean (*link_supports_carrier_detect) (NMPlatform *, int ifindex);
+	gboolean (*link_supports_vlans) (NMPlatform *, int ifindex);
+
+	gboolean (*link_enslave) (NMPlatform *, int master, int slave);
+	gboolean (*link_release) (NMPlatform *, int master, int slave);
+	gboolean (*link_get_master) (NMPlatform *, int slave);
+	gboolean (*master_set_option) (NMPlatform *, int ifindex, const char *option, const char *value);
+	char * (*master_get_option) (NMPlatform *, int ifindex, const char *option);
+	gboolean (*slave_set_option) (NMPlatform *, int ifindex, const char *option, const char *value);
+	char * (*slave_get_option) (NMPlatform *, int ifindex, const char *option);
+
+	gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags);
+	gboolean (*vlan_get_info) (NMPlatform *, int ifindex, int *parent, int *vlan_id);
+	gboolean (*vlan_set_ingress_map) (NMPlatform *, int ifindex, int from, int to);
+	gboolean (*vlan_set_egress_map) (NMPlatform *, int ifindex, int from, int to);
+
+	gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key);
+
+	gboolean (*veth_get_properties) (NMPlatform *, int ifindex, NMPlatformVethProperties *properties);
+	gboolean (*tun_get_properties) (NMPlatform *, int ifindex, NMPlatformTunProperties *properties);
+	gboolean (*macvlan_get_properties) (NMPlatform *, int ifindex, NMPlatformMacvlanProperties *props);
+	gboolean (*vxlan_get_properties) (NMPlatform *, int ifindex, NMPlatformVxlanProperties *props);
+	gboolean (*gre_get_properties) (NMPlatform *, int ifindex, NMPlatformGreProperties *props);
+
+	gboolean    (*wifi_get_capabilities) (NMPlatform *, int ifindex, NMDeviceWifiCapabilities *caps);
+	gboolean    (*wifi_get_bssid)        (NMPlatform *, int ifindex, struct ether_addr *bssid);
+	GByteArray *(*wifi_get_ssid)         (NMPlatform *, int ifindex);
+	guint32     (*wifi_get_frequency)    (NMPlatform *, int ifindex);
+	int         (*wifi_get_quality)      (NMPlatform *, int ifindex);
+	guint32     (*wifi_get_rate)         (NMPlatform *, int ifindex);
+	NM80211Mode (*wifi_get_mode)         (NMPlatform *, int ifindex);
+	void        (*wifi_set_mode)         (NMPlatform *, int ifindex, NM80211Mode mode);
+	guint32     (*wifi_find_frequency)   (NMPlatform *, int ifindex, const guint32 *freqs);
+	void        (*wifi_indicate_addressing_running) (NMPlatform *, int ifindex, gboolean running);
+
+	guint32     (*mesh_get_channel)      (NMPlatform *, int ifindex);
+	gboolean    (*mesh_set_channel)      (NMPlatform *, int ifindex, guint32 channel);
+	gboolean    (*mesh_set_ssid)         (NMPlatform *, int ifindex, const GByteArray *ssid);
+
+	GArray * (*ip4_address_get_all) (NMPlatform *, int ifindex);
+	GArray * (*ip6_address_get_all) (NMPlatform *, int ifindex);
+	gboolean (*ip4_address_add) (NMPlatform *, int ifindex,
+	                             in_addr_t address, in_addr_t peer_address, int plen,
+	                             guint32 lifetime, guint32 preferred_lft,
+	                             const char *label);
+	gboolean (*ip6_address_add) (NMPlatform *, int ifindex,
+	                             struct in6_addr address, struct in6_addr peer_address, int plen,
+	                             guint32 lifetime, guint32 preferred_lft, guint flags);
+	gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, int plen);
+	gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, int plen);
+	gboolean (*ip4_address_exists) (NMPlatform *, int ifindex, in_addr_t address, int plen);
+	gboolean (*ip6_address_exists) (NMPlatform *, int ifindex, struct in6_addr address, int plen);
+
+	GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, gboolean include_default);
+	GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, gboolean include_default);
+	gboolean (*ip4_route_add) (NMPlatform *, int ifindex, NMPlatformSource source,
+	                           in_addr_t network, int plen, in_addr_t gateway,
+	                           int prio, int mss);
+	gboolean (*ip6_route_add) (NMPlatform *, int ifindex, NMPlatformSource source,
+	                           struct in6_addr network, int plen, struct in6_addr gateway,
+	                           int prio, int mss);
+	gboolean (*ip4_route_delete) (NMPlatform *, int ifindex, in_addr_t network, int plen, int metric);
+	gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, int plen, int metric);
+	gboolean (*ip4_route_exists) (NMPlatform *, int ifindex, in_addr_t network, int plen, int metric);
+	gboolean (*ip6_route_exists) (NMPlatform *, int ifindex, struct in6_addr network, int plen, int metric);
+
+	gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *);
+} NMPlatformClass;
+
+/* NMPlatform signals
+ *
+ * Each signal handler is called with a type-specific object that provides
+ * key attributes that constitute identity of the object. They may also
+ * provide additional attributes for convenience.
+ *
+ * The object only intended to be used by the signal handler to determine
+ * the current values. It is no longer valid after the signal handler exits
+ * but you are free to copy the provided information and use it for later
+ * reference.
+ */
+#define NM_PLATFORM_SIGNAL_LINK_CHANGED "link-changed"
+#define NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED "ip4-address-changed"
+#define NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED "ip6-address-changed"
+#define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed"
+#define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed"
+
+/******************************************************************/
+
+GType nm_platform_get_type (void);
+
+void nm_platform_setup (GType type);
+NMPlatform *nm_platform_get (void);
+void nm_platform_free (void);
+
+/******************************************************************/
+
+void nm_platform_set_error (NMPlatformError error);
+NMPlatformError nm_platform_get_error (void);
+const char *nm_platform_get_error_msg (void);
+
+void nm_platform_query_devices (void);
+
+gboolean nm_platform_sysctl_set (const char *path, const char *value);
+char *nm_platform_sysctl_get (const char *path);
+gint32 nm_platform_sysctl_get_int32 (const char *path, gint32 fallback);
+gint64 nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gint64 max, gint64 fallback);
+
+gboolean nm_platform_link_get (int ifindex, NMPlatformLink *link);
+GArray *nm_platform_link_get_all (void);
+gboolean nm_platform_dummy_add (const char *name);
+gboolean nm_platform_bridge_add (const char *name, const void *address, size_t address_len);
+gboolean nm_platform_bond_add (const char *name);
+gboolean nm_platform_team_add (const char *name);
+gboolean nm_platform_link_exists (const char *name);
+gboolean nm_platform_link_delete (int ifindex);
+int nm_platform_link_get_ifindex (const char *name);
+const char *nm_platform_link_get_name (int ifindex);
+NMLinkType nm_platform_link_get_type (int ifindex);
+const char *nm_platform_link_get_type_name (int ifindex);
+gboolean nm_platform_link_is_software (int ifindex);
+gboolean nm_platform_link_supports_slaves (int ifindex);
+
+gboolean nm_platform_link_refresh (int ifindex);
+
+gboolean nm_platform_link_set_up (int ifindex);
+gboolean nm_platform_link_set_down (int ifindex);
+gboolean nm_platform_link_set_arp (int ifindex);
+gboolean nm_platform_link_set_noarp (int ifindex);
+gboolean nm_platform_link_is_up (int ifindex);
+gboolean nm_platform_link_is_connected (int ifindex);
+gboolean nm_platform_link_uses_arp (int ifindex);
+
+gconstpointer nm_platform_link_get_address (int ifindex, size_t *length);
+gboolean nm_platform_link_set_address (int ifindex, const void *address, size_t length);
+guint32 nm_platform_link_get_mtu (int ifindex);
+gboolean nm_platform_link_set_mtu (int ifindex, guint32 mtu);
+
+char    *nm_platform_link_get_physical_port_id (int ifindex);
+gboolean nm_platform_link_get_wake_on_lan (int ifindex);
+
+gboolean nm_platform_link_supports_carrier_detect (int ifindex);
+gboolean nm_platform_link_supports_vlans (int ifindex);
+
+gboolean nm_platform_link_enslave (int master, int slave);
+gboolean nm_platform_link_release (int master, int slave);
+int nm_platform_link_get_master (int slave);
+gboolean nm_platform_master_set_option (int ifindex, const char *option, const char *value);
+char *nm_platform_master_get_option (int ifindex, const char *option);
+gboolean nm_platform_slave_set_option (int ifindex, const char *option, const char *value);
+char *nm_platform_slave_get_option (int ifindex, const char *option);
+
+gboolean nm_platform_vlan_add (const char *name, int parent, int vlanid, guint32 vlanflags);
+gboolean nm_platform_vlan_get_info (int ifindex, int *parent, int *vlanid);
+gboolean nm_platform_vlan_set_ingress_map (int ifindex, int from, int to);
+gboolean nm_platform_vlan_set_egress_map (int ifindex, int from, int to);
+
+gboolean nm_platform_infiniband_partition_add (int parent, int p_key);
+
+gboolean nm_platform_veth_get_properties (int ifindex, NMPlatformVethProperties *properties);
+gboolean nm_platform_tun_get_properties (int ifindex, NMPlatformTunProperties *properties);
+gboolean nm_platform_macvlan_get_properties (int ifindex, NMPlatformMacvlanProperties *props);
+gboolean nm_platform_vxlan_get_properties (int ifindex, NMPlatformVxlanProperties *props);
+gboolean nm_platform_gre_get_properties (int ifindex, NMPlatformGreProperties *props);
+
+gboolean    nm_platform_wifi_get_capabilities (int ifindex, NMDeviceWifiCapabilities *caps);
+gboolean    nm_platform_wifi_get_bssid        (int ifindex, struct ether_addr *bssid);
+GByteArray *nm_platform_wifi_get_ssid         (int ifindex);
+guint32     nm_platform_wifi_get_frequency    (int ifindex);
+int         nm_platform_wifi_get_quality      (int ifindex);
+guint32     nm_platform_wifi_get_rate         (int ifindex);
+NM80211Mode nm_platform_wifi_get_mode         (int ifindex);
+void        nm_platform_wifi_set_mode         (int ifindex, NM80211Mode mode);
+guint32     nm_platform_wifi_find_frequency   (int ifindex, const guint32 *freqs);
+void        nm_platform_wifi_indicate_addressing_running (int ifindex, gboolean running);
+
+guint32     nm_platform_mesh_get_channel      (int ifindex);
+gboolean    nm_platform_mesh_set_channel      (int ifindex, guint32 channel);
+gboolean    nm_platform_mesh_set_ssid         (int ifindex, const GByteArray *ssid);
+
+GArray *nm_platform_ip4_address_get_all (int ifindex);
+GArray *nm_platform_ip6_address_get_all (int ifindex);
+gboolean nm_platform_ip4_address_add (int ifindex,
+                                      in_addr_t address, in_addr_t peer_address, int plen,
+                                      guint32 lifetime, guint32 preferred_lft,
+                                      const char *label);
+gboolean nm_platform_ip6_address_add (int ifindex,
+                                      struct in6_addr address, struct in6_addr peer_address, int plen,
+                                      guint32 lifetime, guint32 preferred_lft, guint flags);
+gboolean nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen);
+gboolean nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen);
+gboolean nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen);
+gboolean nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen);
+gboolean nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses);
+gboolean nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses);
+gboolean nm_platform_address_flush (int ifindex);
+
+GArray *nm_platform_ip4_route_get_all (int ifindex, gboolean include_default);
+GArray *nm_platform_ip6_route_get_all (int ifindex, gboolean include_default);
+gboolean nm_platform_route_set_metric (int ifindex, int metric);
+gboolean nm_platform_ip4_route_add (int ifindex, NMPlatformSource source,
+                                    in_addr_t network, int plen, in_addr_t gateway,
+                                    int metric, int mss);
+gboolean nm_platform_ip6_route_add (int ifindex, NMPlatformSource source,
+                                    struct in6_addr network, int plen, struct in6_addr gateway,
+                                    int metric, int mss);
+gboolean nm_platform_ip4_route_delete (int ifindex, in_addr_t network, int plen, int metric);
+gboolean nm_platform_ip6_route_delete (int ifindex, struct in6_addr network, int plen, int metric);
+gboolean nm_platform_ip4_route_exists (int ifindex, in_addr_t network, int plen, int metric);
+gboolean nm_platform_ip6_route_exists (int ifindex, struct in6_addr network, int plen, int metric);
+gboolean nm_platform_ip4_route_sync (int ifindex, const GArray *known_routes);
+gboolean nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes);
+gboolean nm_platform_route_flush (int ifindex);
+
+const char *nm_platform_link_to_string (const NMPlatformLink *link);
+const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address);
+const char *nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address);
+const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route);
+const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route);
+
+int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b);
+int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b);
+int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b);
+int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b);
+int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b);
+
+gboolean nm_platform_check_support_libnl_extended_ifa_flags (void);
+gboolean nm_platform_check_support_kernel_extended_ifa_flags (void);
+
+void nm_platform_addr_flags2str (int flags, char *buf, size_t size);
+
+int nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatformIPAddress *b);
+
+#define auto_g_free __attribute__((cleanup(put_g_free)))
+static void __attribute__((unused))
+put_g_free (void *ptr)
+{
+	g_clear_pointer ((gpointer *) ptr, g_free);
+}
+
+#endif /* NM_PLATFORM_H */
diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am
new file mode 100644
index 00000000..9b10ea7b
--- /dev/null
+++ b/src/platform/tests/Makefile.am
@@ -0,0 +1,116 @@
+AM_CPPFLAGS = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/include \
+	-I${top_srcdir}/src \
+	-I${top_builddir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-util \
+	-I${srcdir}/.. \
+	-DG_LOG_DOMAIN=\""NetworkManager"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(GLIB_CFLAGS) \
+	$(GUDEV_CFLAGS) \
+	$(LIBNL_CFLAGS)
+
+if REQUIRE_ROOT_TESTS
+AM_CPPFLAGS += -DREQUIRE_ROOT_TESTS=1
+endif
+
+PLATFORM_SOURCES = \
+	../nm-platform.c \
+	../nm-fake-platform.c \
+	../nm-linux-platform.c
+TEST_SOURCES = $(PLATFORM_SOURCES) test-common.c test-common.h
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_LDFLAGS = $(GLIB_LIBS) $(GUDEV_LIBS) $(LIBNL_LIBS) $(CODE_COVERAGE_LDFLAGS)
+PLATFORM_LDADD = \
+	$(top_builddir)/src/libNetworkManager.la
+
+@GNOME_CODE_COVERAGE_RULES@
+
+noinst_PROGRAMS = \
+	dump \
+	monitor \
+	platform \
+	test-link-fake \
+	test-link-linux \
+	test-address-fake \
+	test-address-linux \
+	test-route-fake \
+	test-route-linux \
+	test-cleanup-fake \
+	test-cleanup-linux
+
+EXTRA_DIST = test-common.h
+
+monitor_SOURCES = monitor.c $(PLATFORM_SOURCES)
+monitor_LDADD = $(PLATFORM_LDADD)
+
+dump_SOURCES = dump.c $(PLATFORM_SOURCES)
+dump_LDADD = $(PLATFORM_LDADD)
+
+platform_SOURCES = platform.c $(PLATFORM_SOURCES)
+platform_LDADD = $(PLATFORM_LDADD)
+
+test_link_fake_SOURCES = test-link.c $(TEST_SOURCES)
+test_link_fake_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_fake_platform_setup \
+	-DKERNEL_HACKS=0
+test_link_fake_LDADD = $(PLATFORM_LDADD)
+
+test_link_linux_SOURCES = test-link.c $(TEST_SOURCES)
+test_link_linux_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_linux_platform_setup \
+	-DKERNEL_HACKS=1
+test_link_linux_LDADD = $(PLATFORM_LDADD)
+
+test_address_fake_SOURCES = test-address.c $(TEST_SOURCES)
+test_address_fake_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_fake_platform_setup \
+	-DKERNEL_HACKS=0
+test_address_fake_LDADD = $(PLATFORM_LDADD)
+
+test_address_linux_SOURCES = test-address.c $(TEST_SOURCES)
+test_address_linux_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_linux_platform_setup \
+	-DKERNEL_HACKS=1
+test_address_linux_LDADD = $(PLATFORM_LDADD)
+
+test_route_fake_SOURCES = test-route.c $(TEST_SOURCES)
+test_route_fake_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_fake_platform_setup \
+	-DKERNEL_HACKS=0
+test_route_fake_LDADD = $(PLATFORM_LDADD)
+
+test_route_linux_SOURCES = test-route.c $(TEST_SOURCES)
+test_route_linux_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_linux_platform_setup \
+	-DKERNEL_HACKS=1
+test_route_linux_LDADD = $(PLATFORM_LDADD)
+
+test_cleanup_fake_SOURCES = test-cleanup.c $(TEST_SOURCES)
+test_cleanup_fake_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_fake_platform_setup \
+	-DKERNEL_HACKS=0
+test_cleanup_fake_LDADD = $(PLATFORM_LDADD)
+
+test_cleanup_linux_SOURCES = test-cleanup.c $(TEST_SOURCES)
+test_cleanup_linux_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_linux_platform_setup \
+	-DKERNEL_HACKS=1
+test_cleanup_linux_LDADD = $(PLATFORM_LDADD)
+
+#@VALGRIND_RULES@
+TESTS = test-link-fake test-address-fake test-route-fake test-cleanup-fake test-link-linux test-address-linux test-route-linux test-cleanup-linux
+
+
diff --git a/src/platform/tests/Makefile.in b/src/platform/tests/Makefile.in
new file mode 100644
index 00000000..6b03e315
--- /dev/null
+++ b/src/platform/tests/Makefile.in
@@ -0,0 +1,1702 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@REQUIRE_ROOT_TESTS_TRUE@am__append_1 = -DREQUIRE_ROOT_TESTS=1
+noinst_PROGRAMS = dump$(EXEEXT) monitor$(EXEEXT) platform$(EXEEXT) \
+	test-link-fake$(EXEEXT) test-link-linux$(EXEEXT) \
+	test-address-fake$(EXEEXT) test-address-linux$(EXEEXT) \
+	test-route-fake$(EXEEXT) test-route-linux$(EXEEXT) \
+	test-cleanup-fake$(EXEEXT) test-cleanup-linux$(EXEEXT)
+TESTS = test-link-fake$(EXEEXT) test-address-fake$(EXEEXT) \
+	test-route-fake$(EXEEXT) test-cleanup-fake$(EXEEXT) \
+	test-link-linux$(EXEEXT) test-address-linux$(EXEEXT) \
+	test-route-linux$(EXEEXT) test-cleanup-linux$(EXEEXT)
+subdir = src/platform/tests
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/build-aux/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am__objects_1 = nm-platform.$(OBJEXT) nm-fake-platform.$(OBJEXT) \
+	nm-linux-platform.$(OBJEXT)
+am_dump_OBJECTS = dump.$(OBJEXT) $(am__objects_1)
+dump_OBJECTS = $(am_dump_OBJECTS)
+dump_DEPENDENCIES = $(PLATFORM_LDADD)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+am_monitor_OBJECTS = monitor.$(OBJEXT) $(am__objects_1)
+monitor_OBJECTS = $(am_monitor_OBJECTS)
+monitor_DEPENDENCIES = $(PLATFORM_LDADD)
+am_platform_OBJECTS = platform.$(OBJEXT) $(am__objects_1)
+platform_OBJECTS = $(am_platform_OBJECTS)
+platform_DEPENDENCIES = $(PLATFORM_LDADD)
+am__objects_2 = test_address_fake-nm-platform.$(OBJEXT) \
+	test_address_fake-nm-fake-platform.$(OBJEXT) \
+	test_address_fake-nm-linux-platform.$(OBJEXT)
+am__objects_3 = $(am__objects_2) \
+	test_address_fake-test-common.$(OBJEXT)
+am_test_address_fake_OBJECTS =  \
+	test_address_fake-test-address.$(OBJEXT) $(am__objects_3)
+test_address_fake_OBJECTS = $(am_test_address_fake_OBJECTS)
+test_address_fake_DEPENDENCIES = $(PLATFORM_LDADD)
+am__objects_4 = test_address_linux-nm-platform.$(OBJEXT) \
+	test_address_linux-nm-fake-platform.$(OBJEXT) \
+	test_address_linux-nm-linux-platform.$(OBJEXT)
+am__objects_5 = $(am__objects_4) \
+	test_address_linux-test-common.$(OBJEXT)
+am_test_address_linux_OBJECTS =  \
+	test_address_linux-test-address.$(OBJEXT) $(am__objects_5)
+test_address_linux_OBJECTS = $(am_test_address_linux_OBJECTS)
+test_address_linux_DEPENDENCIES = $(PLATFORM_LDADD)
+am__objects_6 = test_cleanup_fake-nm-platform.$(OBJEXT) \
+	test_cleanup_fake-nm-fake-platform.$(OBJEXT) \
+	test_cleanup_fake-nm-linux-platform.$(OBJEXT)
+am__objects_7 = $(am__objects_6) \
+	test_cleanup_fake-test-common.$(OBJEXT)
+am_test_cleanup_fake_OBJECTS =  \
+	test_cleanup_fake-test-cleanup.$(OBJEXT) $(am__objects_7)
+test_cleanup_fake_OBJECTS = $(am_test_cleanup_fake_OBJECTS)
+test_cleanup_fake_DEPENDENCIES = $(PLATFORM_LDADD)
+am__objects_8 = test_cleanup_linux-nm-platform.$(OBJEXT) \
+	test_cleanup_linux-nm-fake-platform.$(OBJEXT) \
+	test_cleanup_linux-nm-linux-platform.$(OBJEXT)
+am__objects_9 = $(am__objects_8) \
+	test_cleanup_linux-test-common.$(OBJEXT)
+am_test_cleanup_linux_OBJECTS =  \
+	test_cleanup_linux-test-cleanup.$(OBJEXT) $(am__objects_9)
+test_cleanup_linux_OBJECTS = $(am_test_cleanup_linux_OBJECTS)
+test_cleanup_linux_DEPENDENCIES = $(PLATFORM_LDADD)
+am__objects_10 = test_link_fake-nm-platform.$(OBJEXT) \
+	test_link_fake-nm-fake-platform.$(OBJEXT) \
+	test_link_fake-nm-linux-platform.$(OBJEXT)
+am__objects_11 = $(am__objects_10) \
+	test_link_fake-test-common.$(OBJEXT)
+am_test_link_fake_OBJECTS = test_link_fake-test-link.$(OBJEXT) \
+	$(am__objects_11)
+test_link_fake_OBJECTS = $(am_test_link_fake_OBJECTS)
+test_link_fake_DEPENDENCIES = $(PLATFORM_LDADD)
+am__objects_12 = test_link_linux-nm-platform.$(OBJEXT) \
+	test_link_linux-nm-fake-platform.$(OBJEXT) \
+	test_link_linux-nm-linux-platform.$(OBJEXT)
+am__objects_13 = $(am__objects_12) \
+	test_link_linux-test-common.$(OBJEXT)
+am_test_link_linux_OBJECTS = test_link_linux-test-link.$(OBJEXT) \
+	$(am__objects_13)
+test_link_linux_OBJECTS = $(am_test_link_linux_OBJECTS)
+test_link_linux_DEPENDENCIES = $(PLATFORM_LDADD)
+am__objects_14 = test_route_fake-nm-platform.$(OBJEXT) \
+	test_route_fake-nm-fake-platform.$(OBJEXT) \
+	test_route_fake-nm-linux-platform.$(OBJEXT)
+am__objects_15 = $(am__objects_14) \
+	test_route_fake-test-common.$(OBJEXT)
+am_test_route_fake_OBJECTS = test_route_fake-test-route.$(OBJEXT) \
+	$(am__objects_15)
+test_route_fake_OBJECTS = $(am_test_route_fake_OBJECTS)
+test_route_fake_DEPENDENCIES = $(PLATFORM_LDADD)
+am__objects_16 = test_route_linux-nm-platform.$(OBJEXT) \
+	test_route_linux-nm-fake-platform.$(OBJEXT) \
+	test_route_linux-nm-linux-platform.$(OBJEXT)
+am__objects_17 = $(am__objects_16) \
+	test_route_linux-test-common.$(OBJEXT)
+am_test_route_linux_OBJECTS = test_route_linux-test-route.$(OBJEXT) \
+	$(am__objects_17)
+test_route_linux_OBJECTS = $(am_test_route_linux_OBJECTS)
+test_route_linux_DEPENDENCIES = $(PLATFORM_LDADD)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(dump_SOURCES) $(monitor_SOURCES) $(platform_SOURCES) \
+	$(test_address_fake_SOURCES) $(test_address_linux_SOURCES) \
+	$(test_cleanup_fake_SOURCES) $(test_cleanup_linux_SOURCES) \
+	$(test_link_fake_SOURCES) $(test_link_linux_SOURCES) \
+	$(test_route_fake_SOURCES) $(test_route_linux_SOURCES)
+DIST_SOURCES = $(dump_SOURCES) $(monitor_SOURCES) $(platform_SOURCES) \
+	$(test_address_fake_SOURCES) $(test_address_linux_SOURCES) \
+	$(test_cleanup_fake_SOURCES) $(test_cleanup_linux_SOURCES) \
+	$(test_link_fake_SOURCES) $(test_link_linux_SOURCES) \
+	$(test_route_fake_SOURCES) $(test_route_linux_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
+DLLTOOL = @DLLTOOL@
+DNSMASQ_PATH = @DNSMASQ_PATH@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MAKEFILE = @GLIB_MAKEFILE@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+IPTABLES_PATH = @IPTABLES_PATH@
+IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
+IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
+KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
+MOC = @MOC@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
+NM_MICRO_VERSION = @NM_MICRO_VERSION@
+NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
+NM_VERSION = @NM_VERSION@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
+QT_CFLAGS = @QT_CFLAGS@
+QT_LIBS = @QT_LIBS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@
+SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
+AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/include \
+	-I${top_builddir}/include -I${top_srcdir}/src \
+	-I${top_builddir}/src -I${top_srcdir}/src/logging \
+	-I${top_srcdir}/libnm-util -I${top_builddir}/libnm-util \
+	-I${srcdir}/.. -DG_LOG_DOMAIN=\""NetworkManager"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE $(GLIB_CFLAGS) \
+	$(GUDEV_CFLAGS) $(LIBNL_CFLAGS) $(am__append_1)
+PLATFORM_SOURCES = \
+	../nm-platform.c \
+	../nm-fake-platform.c \
+	../nm-linux-platform.c
+
+TEST_SOURCES = $(PLATFORM_SOURCES) test-common.c test-common.h
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_LDFLAGS = $(GLIB_LIBS) $(GUDEV_LIBS) $(LIBNL_LIBS) $(CODE_COVERAGE_LDFLAGS)
+PLATFORM_LDADD = \
+	$(top_builddir)/src/libNetworkManager.la
+
+EXTRA_DIST = test-common.h
+monitor_SOURCES = monitor.c $(PLATFORM_SOURCES)
+monitor_LDADD = $(PLATFORM_LDADD)
+dump_SOURCES = dump.c $(PLATFORM_SOURCES)
+dump_LDADD = $(PLATFORM_LDADD)
+platform_SOURCES = platform.c $(PLATFORM_SOURCES)
+platform_LDADD = $(PLATFORM_LDADD)
+test_link_fake_SOURCES = test-link.c $(TEST_SOURCES)
+test_link_fake_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_fake_platform_setup \
+	-DKERNEL_HACKS=0
+
+test_link_fake_LDADD = $(PLATFORM_LDADD)
+test_link_linux_SOURCES = test-link.c $(TEST_SOURCES)
+test_link_linux_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_linux_platform_setup \
+	-DKERNEL_HACKS=1
+
+test_link_linux_LDADD = $(PLATFORM_LDADD)
+test_address_fake_SOURCES = test-address.c $(TEST_SOURCES)
+test_address_fake_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_fake_platform_setup \
+	-DKERNEL_HACKS=0
+
+test_address_fake_LDADD = $(PLATFORM_LDADD)
+test_address_linux_SOURCES = test-address.c $(TEST_SOURCES)
+test_address_linux_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_linux_platform_setup \
+	-DKERNEL_HACKS=1
+
+test_address_linux_LDADD = $(PLATFORM_LDADD)
+test_route_fake_SOURCES = test-route.c $(TEST_SOURCES)
+test_route_fake_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_fake_platform_setup \
+	-DKERNEL_HACKS=0
+
+test_route_fake_LDADD = $(PLATFORM_LDADD)
+test_route_linux_SOURCES = test-route.c $(TEST_SOURCES)
+test_route_linux_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_linux_platform_setup \
+	-DKERNEL_HACKS=1
+
+test_route_linux_LDADD = $(PLATFORM_LDADD)
+test_cleanup_fake_SOURCES = test-cleanup.c $(TEST_SOURCES)
+test_cleanup_fake_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_fake_platform_setup \
+	-DKERNEL_HACKS=0
+
+test_cleanup_fake_LDADD = $(PLATFORM_LDADD)
+test_cleanup_linux_SOURCES = test-cleanup.c $(TEST_SOURCES)
+test_cleanup_linux_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-DSETUP=nm_linux_platform_setup \
+	-DKERNEL_HACKS=1
+
+test_cleanup_linux_LDADD = $(PLATFORM_LDADD)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/platform/tests/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/platform/tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+dump$(EXEEXT): $(dump_OBJECTS) $(dump_DEPENDENCIES) $(EXTRA_dump_DEPENDENCIES) 
+	@rm -f dump$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(dump_OBJECTS) $(dump_LDADD) $(LIBS)
+
+monitor$(EXEEXT): $(monitor_OBJECTS) $(monitor_DEPENDENCIES) $(EXTRA_monitor_DEPENDENCIES) 
+	@rm -f monitor$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(monitor_OBJECTS) $(monitor_LDADD) $(LIBS)
+
+platform$(EXEEXT): $(platform_OBJECTS) $(platform_DEPENDENCIES) $(EXTRA_platform_DEPENDENCIES) 
+	@rm -f platform$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(platform_OBJECTS) $(platform_LDADD) $(LIBS)
+
+test-address-fake$(EXEEXT): $(test_address_fake_OBJECTS) $(test_address_fake_DEPENDENCIES) $(EXTRA_test_address_fake_DEPENDENCIES) 
+	@rm -f test-address-fake$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_address_fake_OBJECTS) $(test_address_fake_LDADD) $(LIBS)
+
+test-address-linux$(EXEEXT): $(test_address_linux_OBJECTS) $(test_address_linux_DEPENDENCIES) $(EXTRA_test_address_linux_DEPENDENCIES) 
+	@rm -f test-address-linux$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_address_linux_OBJECTS) $(test_address_linux_LDADD) $(LIBS)
+
+test-cleanup-fake$(EXEEXT): $(test_cleanup_fake_OBJECTS) $(test_cleanup_fake_DEPENDENCIES) $(EXTRA_test_cleanup_fake_DEPENDENCIES) 
+	@rm -f test-cleanup-fake$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_cleanup_fake_OBJECTS) $(test_cleanup_fake_LDADD) $(LIBS)
+
+test-cleanup-linux$(EXEEXT): $(test_cleanup_linux_OBJECTS) $(test_cleanup_linux_DEPENDENCIES) $(EXTRA_test_cleanup_linux_DEPENDENCIES) 
+	@rm -f test-cleanup-linux$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_cleanup_linux_OBJECTS) $(test_cleanup_linux_LDADD) $(LIBS)
+
+test-link-fake$(EXEEXT): $(test_link_fake_OBJECTS) $(test_link_fake_DEPENDENCIES) $(EXTRA_test_link_fake_DEPENDENCIES) 
+	@rm -f test-link-fake$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_link_fake_OBJECTS) $(test_link_fake_LDADD) $(LIBS)
+
+test-link-linux$(EXEEXT): $(test_link_linux_OBJECTS) $(test_link_linux_DEPENDENCIES) $(EXTRA_test_link_linux_DEPENDENCIES) 
+	@rm -f test-link-linux$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_link_linux_OBJECTS) $(test_link_linux_LDADD) $(LIBS)
+
+test-route-fake$(EXEEXT): $(test_route_fake_OBJECTS) $(test_route_fake_DEPENDENCIES) $(EXTRA_test_route_fake_DEPENDENCIES) 
+	@rm -f test-route-fake$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_route_fake_OBJECTS) $(test_route_fake_LDADD) $(LIBS)
+
+test-route-linux$(EXEEXT): $(test_route_linux_OBJECTS) $(test_route_linux_DEPENDENCIES) $(EXTRA_test_route_linux_DEPENDENCIES) 
+	@rm -f test-route-linux$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_route_linux_OBJECTS) $(test_route_linux_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/monitor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-fake-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-linux-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-nm-fake-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-nm-linux-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-nm-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-test-address.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_linux-nm-fake-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_linux-nm-linux-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_linux-nm-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_linux-test-address.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_linux-test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_fake-nm-fake-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_fake-nm-linux-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_fake-nm-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_fake-test-cleanup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_fake-test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_linux-nm-fake-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_linux-nm-linux-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_linux-nm-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_linux-test-cleanup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cleanup_linux-test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_fake-nm-fake-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_fake-nm-linux-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_fake-nm-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_fake-test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_fake-test-link.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_linux-nm-fake-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_linux-nm-linux-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_linux-nm-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_linux-test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_link_linux-test-link.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_fake-nm-fake-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_fake-nm-linux-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_fake-nm-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_fake-test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_fake-test-route.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_linux-nm-fake-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_linux-nm-linux-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_linux-nm-platform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_linux-test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_linux-test-route.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+nm-platform.o: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-platform.o -MD -MP -MF $(DEPDIR)/nm-platform.Tpo -c -o nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-platform.Tpo $(DEPDIR)/nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='nm-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+
+nm-platform.obj: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-platform.obj -MD -MP -MF $(DEPDIR)/nm-platform.Tpo -c -o nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-platform.Tpo $(DEPDIR)/nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='nm-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+
+nm-fake-platform.o: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-fake-platform.o -MD -MP -MF $(DEPDIR)/nm-fake-platform.Tpo -c -o nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-fake-platform.Tpo $(DEPDIR)/nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='nm-fake-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+
+nm-fake-platform.obj: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-fake-platform.obj -MD -MP -MF $(DEPDIR)/nm-fake-platform.Tpo -c -o nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-fake-platform.Tpo $(DEPDIR)/nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='nm-fake-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+
+nm-linux-platform.o: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-linux-platform.o -MD -MP -MF $(DEPDIR)/nm-linux-platform.Tpo -c -o nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-linux-platform.Tpo $(DEPDIR)/nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='nm-linux-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+
+nm-linux-platform.obj: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-linux-platform.obj -MD -MP -MF $(DEPDIR)/nm-linux-platform.Tpo -c -o nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-linux-platform.Tpo $(DEPDIR)/nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='nm-linux-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+
+test_address_fake-test-address.o: test-address.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-test-address.o -MD -MP -MF $(DEPDIR)/test_address_fake-test-address.Tpo -c -o test_address_fake-test-address.o `test -f 'test-address.c' || echo '$(srcdir)/'`test-address.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-test-address.Tpo $(DEPDIR)/test_address_fake-test-address.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-address.c' object='test_address_fake-test-address.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-test-address.o `test -f 'test-address.c' || echo '$(srcdir)/'`test-address.c
+
+test_address_fake-test-address.obj: test-address.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-test-address.obj -MD -MP -MF $(DEPDIR)/test_address_fake-test-address.Tpo -c -o test_address_fake-test-address.obj `if test -f 'test-address.c'; then $(CYGPATH_W) 'test-address.c'; else $(CYGPATH_W) '$(srcdir)/test-address.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-test-address.Tpo $(DEPDIR)/test_address_fake-test-address.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-address.c' object='test_address_fake-test-address.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-test-address.obj `if test -f 'test-address.c'; then $(CYGPATH_W) 'test-address.c'; else $(CYGPATH_W) '$(srcdir)/test-address.c'; fi`
+
+test_address_fake-nm-platform.o: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-nm-platform.o -MD -MP -MF $(DEPDIR)/test_address_fake-nm-platform.Tpo -c -o test_address_fake-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-nm-platform.Tpo $(DEPDIR)/test_address_fake-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_address_fake-nm-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+
+test_address_fake-nm-platform.obj: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-nm-platform.obj -MD -MP -MF $(DEPDIR)/test_address_fake-nm-platform.Tpo -c -o test_address_fake-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-nm-platform.Tpo $(DEPDIR)/test_address_fake-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_address_fake-nm-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+
+test_address_fake-nm-fake-platform.o: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-nm-fake-platform.o -MD -MP -MF $(DEPDIR)/test_address_fake-nm-fake-platform.Tpo -c -o test_address_fake-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-nm-fake-platform.Tpo $(DEPDIR)/test_address_fake-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_address_fake-nm-fake-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+
+test_address_fake-nm-fake-platform.obj: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-nm-fake-platform.obj -MD -MP -MF $(DEPDIR)/test_address_fake-nm-fake-platform.Tpo -c -o test_address_fake-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-nm-fake-platform.Tpo $(DEPDIR)/test_address_fake-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_address_fake-nm-fake-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+
+test_address_fake-nm-linux-platform.o: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-nm-linux-platform.o -MD -MP -MF $(DEPDIR)/test_address_fake-nm-linux-platform.Tpo -c -o test_address_fake-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-nm-linux-platform.Tpo $(DEPDIR)/test_address_fake-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_address_fake-nm-linux-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+
+test_address_fake-nm-linux-platform.obj: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-nm-linux-platform.obj -MD -MP -MF $(DEPDIR)/test_address_fake-nm-linux-platform.Tpo -c -o test_address_fake-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-nm-linux-platform.Tpo $(DEPDIR)/test_address_fake-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_address_fake-nm-linux-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+
+test_address_fake-test-common.o: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-test-common.o -MD -MP -MF $(DEPDIR)/test_address_fake-test-common.Tpo -c -o test_address_fake-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-test-common.Tpo $(DEPDIR)/test_address_fake-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_address_fake-test-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+
+test_address_fake-test-common.obj: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_fake-test-common.obj -MD -MP -MF $(DEPDIR)/test_address_fake-test-common.Tpo -c -o test_address_fake-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_fake-test-common.Tpo $(DEPDIR)/test_address_fake-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_address_fake-test-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_fake-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+
+test_address_linux-test-address.o: test-address.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-test-address.o -MD -MP -MF $(DEPDIR)/test_address_linux-test-address.Tpo -c -o test_address_linux-test-address.o `test -f 'test-address.c' || echo '$(srcdir)/'`test-address.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-test-address.Tpo $(DEPDIR)/test_address_linux-test-address.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-address.c' object='test_address_linux-test-address.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-test-address.o `test -f 'test-address.c' || echo '$(srcdir)/'`test-address.c
+
+test_address_linux-test-address.obj: test-address.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-test-address.obj -MD -MP -MF $(DEPDIR)/test_address_linux-test-address.Tpo -c -o test_address_linux-test-address.obj `if test -f 'test-address.c'; then $(CYGPATH_W) 'test-address.c'; else $(CYGPATH_W) '$(srcdir)/test-address.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-test-address.Tpo $(DEPDIR)/test_address_linux-test-address.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-address.c' object='test_address_linux-test-address.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-test-address.obj `if test -f 'test-address.c'; then $(CYGPATH_W) 'test-address.c'; else $(CYGPATH_W) '$(srcdir)/test-address.c'; fi`
+
+test_address_linux-nm-platform.o: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-nm-platform.o -MD -MP -MF $(DEPDIR)/test_address_linux-nm-platform.Tpo -c -o test_address_linux-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-nm-platform.Tpo $(DEPDIR)/test_address_linux-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_address_linux-nm-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+
+test_address_linux-nm-platform.obj: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-nm-platform.obj -MD -MP -MF $(DEPDIR)/test_address_linux-nm-platform.Tpo -c -o test_address_linux-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-nm-platform.Tpo $(DEPDIR)/test_address_linux-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_address_linux-nm-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+
+test_address_linux-nm-fake-platform.o: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-nm-fake-platform.o -MD -MP -MF $(DEPDIR)/test_address_linux-nm-fake-platform.Tpo -c -o test_address_linux-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-nm-fake-platform.Tpo $(DEPDIR)/test_address_linux-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_address_linux-nm-fake-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+
+test_address_linux-nm-fake-platform.obj: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-nm-fake-platform.obj -MD -MP -MF $(DEPDIR)/test_address_linux-nm-fake-platform.Tpo -c -o test_address_linux-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-nm-fake-platform.Tpo $(DEPDIR)/test_address_linux-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_address_linux-nm-fake-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+
+test_address_linux-nm-linux-platform.o: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-nm-linux-platform.o -MD -MP -MF $(DEPDIR)/test_address_linux-nm-linux-platform.Tpo -c -o test_address_linux-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-nm-linux-platform.Tpo $(DEPDIR)/test_address_linux-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_address_linux-nm-linux-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+
+test_address_linux-nm-linux-platform.obj: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-nm-linux-platform.obj -MD -MP -MF $(DEPDIR)/test_address_linux-nm-linux-platform.Tpo -c -o test_address_linux-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-nm-linux-platform.Tpo $(DEPDIR)/test_address_linux-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_address_linux-nm-linux-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+
+test_address_linux-test-common.o: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-test-common.o -MD -MP -MF $(DEPDIR)/test_address_linux-test-common.Tpo -c -o test_address_linux-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-test-common.Tpo $(DEPDIR)/test_address_linux-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_address_linux-test-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+
+test_address_linux-test-common.obj: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_address_linux-test-common.obj -MD -MP -MF $(DEPDIR)/test_address_linux-test-common.Tpo -c -o test_address_linux-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_address_linux-test-common.Tpo $(DEPDIR)/test_address_linux-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_address_linux-test-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_address_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_address_linux-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+
+test_cleanup_fake-test-cleanup.o: test-cleanup.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-test-cleanup.o -MD -MP -MF $(DEPDIR)/test_cleanup_fake-test-cleanup.Tpo -c -o test_cleanup_fake-test-cleanup.o `test -f 'test-cleanup.c' || echo '$(srcdir)/'`test-cleanup.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-test-cleanup.Tpo $(DEPDIR)/test_cleanup_fake-test-cleanup.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-cleanup.c' object='test_cleanup_fake-test-cleanup.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-test-cleanup.o `test -f 'test-cleanup.c' || echo '$(srcdir)/'`test-cleanup.c
+
+test_cleanup_fake-test-cleanup.obj: test-cleanup.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-test-cleanup.obj -MD -MP -MF $(DEPDIR)/test_cleanup_fake-test-cleanup.Tpo -c -o test_cleanup_fake-test-cleanup.obj `if test -f 'test-cleanup.c'; then $(CYGPATH_W) 'test-cleanup.c'; else $(CYGPATH_W) '$(srcdir)/test-cleanup.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-test-cleanup.Tpo $(DEPDIR)/test_cleanup_fake-test-cleanup.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-cleanup.c' object='test_cleanup_fake-test-cleanup.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-test-cleanup.obj `if test -f 'test-cleanup.c'; then $(CYGPATH_W) 'test-cleanup.c'; else $(CYGPATH_W) '$(srcdir)/test-cleanup.c'; fi`
+
+test_cleanup_fake-nm-platform.o: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-nm-platform.o -MD -MP -MF $(DEPDIR)/test_cleanup_fake-nm-platform.Tpo -c -o test_cleanup_fake-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-nm-platform.Tpo $(DEPDIR)/test_cleanup_fake-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_cleanup_fake-nm-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+
+test_cleanup_fake-nm-platform.obj: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-nm-platform.obj -MD -MP -MF $(DEPDIR)/test_cleanup_fake-nm-platform.Tpo -c -o test_cleanup_fake-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-nm-platform.Tpo $(DEPDIR)/test_cleanup_fake-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_cleanup_fake-nm-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+
+test_cleanup_fake-nm-fake-platform.o: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-nm-fake-platform.o -MD -MP -MF $(DEPDIR)/test_cleanup_fake-nm-fake-platform.Tpo -c -o test_cleanup_fake-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-nm-fake-platform.Tpo $(DEPDIR)/test_cleanup_fake-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_cleanup_fake-nm-fake-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+
+test_cleanup_fake-nm-fake-platform.obj: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-nm-fake-platform.obj -MD -MP -MF $(DEPDIR)/test_cleanup_fake-nm-fake-platform.Tpo -c -o test_cleanup_fake-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-nm-fake-platform.Tpo $(DEPDIR)/test_cleanup_fake-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_cleanup_fake-nm-fake-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+
+test_cleanup_fake-nm-linux-platform.o: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-nm-linux-platform.o -MD -MP -MF $(DEPDIR)/test_cleanup_fake-nm-linux-platform.Tpo -c -o test_cleanup_fake-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-nm-linux-platform.Tpo $(DEPDIR)/test_cleanup_fake-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_cleanup_fake-nm-linux-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+
+test_cleanup_fake-nm-linux-platform.obj: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-nm-linux-platform.obj -MD -MP -MF $(DEPDIR)/test_cleanup_fake-nm-linux-platform.Tpo -c -o test_cleanup_fake-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-nm-linux-platform.Tpo $(DEPDIR)/test_cleanup_fake-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_cleanup_fake-nm-linux-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+
+test_cleanup_fake-test-common.o: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-test-common.o -MD -MP -MF $(DEPDIR)/test_cleanup_fake-test-common.Tpo -c -o test_cleanup_fake-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-test-common.Tpo $(DEPDIR)/test_cleanup_fake-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_cleanup_fake-test-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+
+test_cleanup_fake-test-common.obj: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_fake-test-common.obj -MD -MP -MF $(DEPDIR)/test_cleanup_fake-test-common.Tpo -c -o test_cleanup_fake-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_fake-test-common.Tpo $(DEPDIR)/test_cleanup_fake-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_cleanup_fake-test-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_fake-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+
+test_cleanup_linux-test-cleanup.o: test-cleanup.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-test-cleanup.o -MD -MP -MF $(DEPDIR)/test_cleanup_linux-test-cleanup.Tpo -c -o test_cleanup_linux-test-cleanup.o `test -f 'test-cleanup.c' || echo '$(srcdir)/'`test-cleanup.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-test-cleanup.Tpo $(DEPDIR)/test_cleanup_linux-test-cleanup.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-cleanup.c' object='test_cleanup_linux-test-cleanup.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-test-cleanup.o `test -f 'test-cleanup.c' || echo '$(srcdir)/'`test-cleanup.c
+
+test_cleanup_linux-test-cleanup.obj: test-cleanup.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-test-cleanup.obj -MD -MP -MF $(DEPDIR)/test_cleanup_linux-test-cleanup.Tpo -c -o test_cleanup_linux-test-cleanup.obj `if test -f 'test-cleanup.c'; then $(CYGPATH_W) 'test-cleanup.c'; else $(CYGPATH_W) '$(srcdir)/test-cleanup.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-test-cleanup.Tpo $(DEPDIR)/test_cleanup_linux-test-cleanup.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-cleanup.c' object='test_cleanup_linux-test-cleanup.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-test-cleanup.obj `if test -f 'test-cleanup.c'; then $(CYGPATH_W) 'test-cleanup.c'; else $(CYGPATH_W) '$(srcdir)/test-cleanup.c'; fi`
+
+test_cleanup_linux-nm-platform.o: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-nm-platform.o -MD -MP -MF $(DEPDIR)/test_cleanup_linux-nm-platform.Tpo -c -o test_cleanup_linux-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-nm-platform.Tpo $(DEPDIR)/test_cleanup_linux-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_cleanup_linux-nm-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+
+test_cleanup_linux-nm-platform.obj: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-nm-platform.obj -MD -MP -MF $(DEPDIR)/test_cleanup_linux-nm-platform.Tpo -c -o test_cleanup_linux-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-nm-platform.Tpo $(DEPDIR)/test_cleanup_linux-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_cleanup_linux-nm-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+
+test_cleanup_linux-nm-fake-platform.o: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-nm-fake-platform.o -MD -MP -MF $(DEPDIR)/test_cleanup_linux-nm-fake-platform.Tpo -c -o test_cleanup_linux-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-nm-fake-platform.Tpo $(DEPDIR)/test_cleanup_linux-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_cleanup_linux-nm-fake-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+
+test_cleanup_linux-nm-fake-platform.obj: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-nm-fake-platform.obj -MD -MP -MF $(DEPDIR)/test_cleanup_linux-nm-fake-platform.Tpo -c -o test_cleanup_linux-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-nm-fake-platform.Tpo $(DEPDIR)/test_cleanup_linux-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_cleanup_linux-nm-fake-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+
+test_cleanup_linux-nm-linux-platform.o: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-nm-linux-platform.o -MD -MP -MF $(DEPDIR)/test_cleanup_linux-nm-linux-platform.Tpo -c -o test_cleanup_linux-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-nm-linux-platform.Tpo $(DEPDIR)/test_cleanup_linux-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_cleanup_linux-nm-linux-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+
+test_cleanup_linux-nm-linux-platform.obj: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-nm-linux-platform.obj -MD -MP -MF $(DEPDIR)/test_cleanup_linux-nm-linux-platform.Tpo -c -o test_cleanup_linux-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-nm-linux-platform.Tpo $(DEPDIR)/test_cleanup_linux-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_cleanup_linux-nm-linux-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+
+test_cleanup_linux-test-common.o: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-test-common.o -MD -MP -MF $(DEPDIR)/test_cleanup_linux-test-common.Tpo -c -o test_cleanup_linux-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-test-common.Tpo $(DEPDIR)/test_cleanup_linux-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_cleanup_linux-test-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+
+test_cleanup_linux-test-common.obj: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_cleanup_linux-test-common.obj -MD -MP -MF $(DEPDIR)/test_cleanup_linux-test-common.Tpo -c -o test_cleanup_linux-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_cleanup_linux-test-common.Tpo $(DEPDIR)/test_cleanup_linux-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_cleanup_linux-test-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_cleanup_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_cleanup_linux-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+
+test_link_fake-test-link.o: test-link.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-test-link.o -MD -MP -MF $(DEPDIR)/test_link_fake-test-link.Tpo -c -o test_link_fake-test-link.o `test -f 'test-link.c' || echo '$(srcdir)/'`test-link.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-test-link.Tpo $(DEPDIR)/test_link_fake-test-link.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-link.c' object='test_link_fake-test-link.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-test-link.o `test -f 'test-link.c' || echo '$(srcdir)/'`test-link.c
+
+test_link_fake-test-link.obj: test-link.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-test-link.obj -MD -MP -MF $(DEPDIR)/test_link_fake-test-link.Tpo -c -o test_link_fake-test-link.obj `if test -f 'test-link.c'; then $(CYGPATH_W) 'test-link.c'; else $(CYGPATH_W) '$(srcdir)/test-link.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-test-link.Tpo $(DEPDIR)/test_link_fake-test-link.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-link.c' object='test_link_fake-test-link.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-test-link.obj `if test -f 'test-link.c'; then $(CYGPATH_W) 'test-link.c'; else $(CYGPATH_W) '$(srcdir)/test-link.c'; fi`
+
+test_link_fake-nm-platform.o: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-nm-platform.o -MD -MP -MF $(DEPDIR)/test_link_fake-nm-platform.Tpo -c -o test_link_fake-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-nm-platform.Tpo $(DEPDIR)/test_link_fake-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_link_fake-nm-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+
+test_link_fake-nm-platform.obj: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-nm-platform.obj -MD -MP -MF $(DEPDIR)/test_link_fake-nm-platform.Tpo -c -o test_link_fake-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-nm-platform.Tpo $(DEPDIR)/test_link_fake-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_link_fake-nm-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+
+test_link_fake-nm-fake-platform.o: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-nm-fake-platform.o -MD -MP -MF $(DEPDIR)/test_link_fake-nm-fake-platform.Tpo -c -o test_link_fake-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-nm-fake-platform.Tpo $(DEPDIR)/test_link_fake-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_link_fake-nm-fake-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+
+test_link_fake-nm-fake-platform.obj: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-nm-fake-platform.obj -MD -MP -MF $(DEPDIR)/test_link_fake-nm-fake-platform.Tpo -c -o test_link_fake-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-nm-fake-platform.Tpo $(DEPDIR)/test_link_fake-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_link_fake-nm-fake-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+
+test_link_fake-nm-linux-platform.o: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-nm-linux-platform.o -MD -MP -MF $(DEPDIR)/test_link_fake-nm-linux-platform.Tpo -c -o test_link_fake-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-nm-linux-platform.Tpo $(DEPDIR)/test_link_fake-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_link_fake-nm-linux-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+
+test_link_fake-nm-linux-platform.obj: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-nm-linux-platform.obj -MD -MP -MF $(DEPDIR)/test_link_fake-nm-linux-platform.Tpo -c -o test_link_fake-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-nm-linux-platform.Tpo $(DEPDIR)/test_link_fake-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_link_fake-nm-linux-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+
+test_link_fake-test-common.o: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-test-common.o -MD -MP -MF $(DEPDIR)/test_link_fake-test-common.Tpo -c -o test_link_fake-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-test-common.Tpo $(DEPDIR)/test_link_fake-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_link_fake-test-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+
+test_link_fake-test-common.obj: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_fake-test-common.obj -MD -MP -MF $(DEPDIR)/test_link_fake-test-common.Tpo -c -o test_link_fake-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_fake-test-common.Tpo $(DEPDIR)/test_link_fake-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_link_fake-test-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_fake-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+
+test_link_linux-test-link.o: test-link.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-test-link.o -MD -MP -MF $(DEPDIR)/test_link_linux-test-link.Tpo -c -o test_link_linux-test-link.o `test -f 'test-link.c' || echo '$(srcdir)/'`test-link.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-test-link.Tpo $(DEPDIR)/test_link_linux-test-link.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-link.c' object='test_link_linux-test-link.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-test-link.o `test -f 'test-link.c' || echo '$(srcdir)/'`test-link.c
+
+test_link_linux-test-link.obj: test-link.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-test-link.obj -MD -MP -MF $(DEPDIR)/test_link_linux-test-link.Tpo -c -o test_link_linux-test-link.obj `if test -f 'test-link.c'; then $(CYGPATH_W) 'test-link.c'; else $(CYGPATH_W) '$(srcdir)/test-link.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-test-link.Tpo $(DEPDIR)/test_link_linux-test-link.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-link.c' object='test_link_linux-test-link.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-test-link.obj `if test -f 'test-link.c'; then $(CYGPATH_W) 'test-link.c'; else $(CYGPATH_W) '$(srcdir)/test-link.c'; fi`
+
+test_link_linux-nm-platform.o: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-nm-platform.o -MD -MP -MF $(DEPDIR)/test_link_linux-nm-platform.Tpo -c -o test_link_linux-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-nm-platform.Tpo $(DEPDIR)/test_link_linux-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_link_linux-nm-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+
+test_link_linux-nm-platform.obj: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-nm-platform.obj -MD -MP -MF $(DEPDIR)/test_link_linux-nm-platform.Tpo -c -o test_link_linux-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-nm-platform.Tpo $(DEPDIR)/test_link_linux-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_link_linux-nm-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+
+test_link_linux-nm-fake-platform.o: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-nm-fake-platform.o -MD -MP -MF $(DEPDIR)/test_link_linux-nm-fake-platform.Tpo -c -o test_link_linux-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-nm-fake-platform.Tpo $(DEPDIR)/test_link_linux-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_link_linux-nm-fake-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+
+test_link_linux-nm-fake-platform.obj: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-nm-fake-platform.obj -MD -MP -MF $(DEPDIR)/test_link_linux-nm-fake-platform.Tpo -c -o test_link_linux-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-nm-fake-platform.Tpo $(DEPDIR)/test_link_linux-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_link_linux-nm-fake-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+
+test_link_linux-nm-linux-platform.o: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-nm-linux-platform.o -MD -MP -MF $(DEPDIR)/test_link_linux-nm-linux-platform.Tpo -c -o test_link_linux-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-nm-linux-platform.Tpo $(DEPDIR)/test_link_linux-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_link_linux-nm-linux-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+
+test_link_linux-nm-linux-platform.obj: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-nm-linux-platform.obj -MD -MP -MF $(DEPDIR)/test_link_linux-nm-linux-platform.Tpo -c -o test_link_linux-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-nm-linux-platform.Tpo $(DEPDIR)/test_link_linux-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_link_linux-nm-linux-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+
+test_link_linux-test-common.o: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-test-common.o -MD -MP -MF $(DEPDIR)/test_link_linux-test-common.Tpo -c -o test_link_linux-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-test-common.Tpo $(DEPDIR)/test_link_linux-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_link_linux-test-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+
+test_link_linux-test-common.obj: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_link_linux-test-common.obj -MD -MP -MF $(DEPDIR)/test_link_linux-test-common.Tpo -c -o test_link_linux-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_link_linux-test-common.Tpo $(DEPDIR)/test_link_linux-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_link_linux-test-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_link_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_link_linux-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+
+test_route_fake-test-route.o: test-route.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-test-route.o -MD -MP -MF $(DEPDIR)/test_route_fake-test-route.Tpo -c -o test_route_fake-test-route.o `test -f 'test-route.c' || echo '$(srcdir)/'`test-route.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-test-route.Tpo $(DEPDIR)/test_route_fake-test-route.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-route.c' object='test_route_fake-test-route.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-test-route.o `test -f 'test-route.c' || echo '$(srcdir)/'`test-route.c
+
+test_route_fake-test-route.obj: test-route.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-test-route.obj -MD -MP -MF $(DEPDIR)/test_route_fake-test-route.Tpo -c -o test_route_fake-test-route.obj `if test -f 'test-route.c'; then $(CYGPATH_W) 'test-route.c'; else $(CYGPATH_W) '$(srcdir)/test-route.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-test-route.Tpo $(DEPDIR)/test_route_fake-test-route.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-route.c' object='test_route_fake-test-route.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-test-route.obj `if test -f 'test-route.c'; then $(CYGPATH_W) 'test-route.c'; else $(CYGPATH_W) '$(srcdir)/test-route.c'; fi`
+
+test_route_fake-nm-platform.o: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-nm-platform.o -MD -MP -MF $(DEPDIR)/test_route_fake-nm-platform.Tpo -c -o test_route_fake-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-nm-platform.Tpo $(DEPDIR)/test_route_fake-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_route_fake-nm-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+
+test_route_fake-nm-platform.obj: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-nm-platform.obj -MD -MP -MF $(DEPDIR)/test_route_fake-nm-platform.Tpo -c -o test_route_fake-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-nm-platform.Tpo $(DEPDIR)/test_route_fake-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_route_fake-nm-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+
+test_route_fake-nm-fake-platform.o: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-nm-fake-platform.o -MD -MP -MF $(DEPDIR)/test_route_fake-nm-fake-platform.Tpo -c -o test_route_fake-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-nm-fake-platform.Tpo $(DEPDIR)/test_route_fake-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_route_fake-nm-fake-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+
+test_route_fake-nm-fake-platform.obj: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-nm-fake-platform.obj -MD -MP -MF $(DEPDIR)/test_route_fake-nm-fake-platform.Tpo -c -o test_route_fake-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-nm-fake-platform.Tpo $(DEPDIR)/test_route_fake-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_route_fake-nm-fake-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+
+test_route_fake-nm-linux-platform.o: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-nm-linux-platform.o -MD -MP -MF $(DEPDIR)/test_route_fake-nm-linux-platform.Tpo -c -o test_route_fake-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-nm-linux-platform.Tpo $(DEPDIR)/test_route_fake-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_route_fake-nm-linux-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+
+test_route_fake-nm-linux-platform.obj: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-nm-linux-platform.obj -MD -MP -MF $(DEPDIR)/test_route_fake-nm-linux-platform.Tpo -c -o test_route_fake-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-nm-linux-platform.Tpo $(DEPDIR)/test_route_fake-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_route_fake-nm-linux-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+
+test_route_fake-test-common.o: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-test-common.o -MD -MP -MF $(DEPDIR)/test_route_fake-test-common.Tpo -c -o test_route_fake-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-test-common.Tpo $(DEPDIR)/test_route_fake-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_route_fake-test-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+
+test_route_fake-test-common.obj: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_fake-test-common.obj -MD -MP -MF $(DEPDIR)/test_route_fake-test-common.Tpo -c -o test_route_fake-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_fake-test-common.Tpo $(DEPDIR)/test_route_fake-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_route_fake-test-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_fake-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+
+test_route_linux-test-route.o: test-route.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-test-route.o -MD -MP -MF $(DEPDIR)/test_route_linux-test-route.Tpo -c -o test_route_linux-test-route.o `test -f 'test-route.c' || echo '$(srcdir)/'`test-route.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-test-route.Tpo $(DEPDIR)/test_route_linux-test-route.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-route.c' object='test_route_linux-test-route.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-test-route.o `test -f 'test-route.c' || echo '$(srcdir)/'`test-route.c
+
+test_route_linux-test-route.obj: test-route.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-test-route.obj -MD -MP -MF $(DEPDIR)/test_route_linux-test-route.Tpo -c -o test_route_linux-test-route.obj `if test -f 'test-route.c'; then $(CYGPATH_W) 'test-route.c'; else $(CYGPATH_W) '$(srcdir)/test-route.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-test-route.Tpo $(DEPDIR)/test_route_linux-test-route.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-route.c' object='test_route_linux-test-route.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-test-route.obj `if test -f 'test-route.c'; then $(CYGPATH_W) 'test-route.c'; else $(CYGPATH_W) '$(srcdir)/test-route.c'; fi`
+
+test_route_linux-nm-platform.o: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-nm-platform.o -MD -MP -MF $(DEPDIR)/test_route_linux-nm-platform.Tpo -c -o test_route_linux-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-nm-platform.Tpo $(DEPDIR)/test_route_linux-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_route_linux-nm-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-nm-platform.o `test -f '../nm-platform.c' || echo '$(srcdir)/'`../nm-platform.c
+
+test_route_linux-nm-platform.obj: ../nm-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-nm-platform.obj -MD -MP -MF $(DEPDIR)/test_route_linux-nm-platform.Tpo -c -o test_route_linux-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-nm-platform.Tpo $(DEPDIR)/test_route_linux-nm-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-platform.c' object='test_route_linux-nm-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-nm-platform.obj `if test -f '../nm-platform.c'; then $(CYGPATH_W) '../nm-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-platform.c'; fi`
+
+test_route_linux-nm-fake-platform.o: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-nm-fake-platform.o -MD -MP -MF $(DEPDIR)/test_route_linux-nm-fake-platform.Tpo -c -o test_route_linux-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-nm-fake-platform.Tpo $(DEPDIR)/test_route_linux-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_route_linux-nm-fake-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-nm-fake-platform.o `test -f '../nm-fake-platform.c' || echo '$(srcdir)/'`../nm-fake-platform.c
+
+test_route_linux-nm-fake-platform.obj: ../nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-nm-fake-platform.obj -MD -MP -MF $(DEPDIR)/test_route_linux-nm-fake-platform.Tpo -c -o test_route_linux-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-nm-fake-platform.Tpo $(DEPDIR)/test_route_linux-nm-fake-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-fake-platform.c' object='test_route_linux-nm-fake-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-nm-fake-platform.obj `if test -f '../nm-fake-platform.c'; then $(CYGPATH_W) '../nm-fake-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-fake-platform.c'; fi`
+
+test_route_linux-nm-linux-platform.o: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-nm-linux-platform.o -MD -MP -MF $(DEPDIR)/test_route_linux-nm-linux-platform.Tpo -c -o test_route_linux-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-nm-linux-platform.Tpo $(DEPDIR)/test_route_linux-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_route_linux-nm-linux-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-nm-linux-platform.o `test -f '../nm-linux-platform.c' || echo '$(srcdir)/'`../nm-linux-platform.c
+
+test_route_linux-nm-linux-platform.obj: ../nm-linux-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-nm-linux-platform.obj -MD -MP -MF $(DEPDIR)/test_route_linux-nm-linux-platform.Tpo -c -o test_route_linux-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-nm-linux-platform.Tpo $(DEPDIR)/test_route_linux-nm-linux-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-linux-platform.c' object='test_route_linux-nm-linux-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-nm-linux-platform.obj `if test -f '../nm-linux-platform.c'; then $(CYGPATH_W) '../nm-linux-platform.c'; else $(CYGPATH_W) '$(srcdir)/../nm-linux-platform.c'; fi`
+
+test_route_linux-test-common.o: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-test-common.o -MD -MP -MF $(DEPDIR)/test_route_linux-test-common.Tpo -c -o test_route_linux-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-test-common.Tpo $(DEPDIR)/test_route_linux-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_route_linux-test-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-test-common.o `test -f 'test-common.c' || echo '$(srcdir)/'`test-common.c
+
+test_route_linux-test-common.obj: test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_linux-test-common.obj -MD -MP -MF $(DEPDIR)/test_route_linux-test-common.Tpo -c -o test_route_linux-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_route_linux-test-common.Tpo $(DEPDIR)/test_route_linux-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-common.c' object='test_route_linux-test-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_linux-test-common.obj `if test -f 'test-common.c'; then $(CYGPATH_W) 'test-common.c'; else $(CYGPATH_W) '$(srcdir)/test-common.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
+	srcdir=$(srcdir); export srcdir; \
+	list=' $(TESTS) '; \
+	$(am__tty_colors); \
+	if test -n "$$list"; then \
+	  for tst in $$list; do \
+	    if test -f ./$$tst; then dir=./; \
+	    elif test -f $$tst; then dir=; \
+	    else dir="$(srcdir)/"; fi; \
+	    if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *[\ \	]$$tst[\ \	]*) \
+		xpass=`expr $$xpass + 1`; \
+		failed=`expr $$failed + 1`; \
+		col=$$red; res=XPASS; \
+	      ;; \
+	      *) \
+		col=$$grn; res=PASS; \
+	      ;; \
+	      esac; \
+	    elif test $$? -ne 77; then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *[\ \	]$$tst[\ \	]*) \
+		xfail=`expr $$xfail + 1`; \
+		col=$$lgn; res=XFAIL; \
+	      ;; \
+	      *) \
+		failed=`expr $$failed + 1`; \
+		col=$$red; res=FAIL; \
+	      ;; \
+	      esac; \
+	    else \
+	      skip=`expr $$skip + 1`; \
+	      col=$$blu; res=SKIP; \
+	    fi; \
+	    echo "$${col}$$res$${std}: $$tst"; \
+	  done; \
+	  if test "$$all" -eq 1; then \
+	    tests="test"; \
+	    All=""; \
+	  else \
+	    tests="tests"; \
+	    All="All "; \
+	  fi; \
+	  if test "$$failed" -eq 0; then \
+	    if test "$$xfail" -eq 0; then \
+	      banner="$$All$$all $$tests passed"; \
+	    else \
+	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+	    fi; \
+	  else \
+	    if test "$$xpass" -eq 0; then \
+	      banner="$$failed of $$all $$tests failed"; \
+	    else \
+	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	    fi; \
+	  fi; \
+	  dashes="$$banner"; \
+	  skipped=""; \
+	  if test "$$skip" -ne 0; then \
+	    if test "$$skip" -eq 1; then \
+	      skipped="($$skip test was not run)"; \
+	    else \
+	      skipped="($$skip tests were not run)"; \
+	    fi; \
+	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+	      dashes="$$skipped"; \
+	  fi; \
+	  report=""; \
+	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+	    report="Please report to $(PACKAGE_BUGREPORT)"; \
+	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+	      dashes="$$report"; \
+	  fi; \
+	  dashes=`echo "$$dashes" | sed s/./=/g`; \
+	  if test "$$failed" -eq 0; then \
+	    col="$$grn"; \
+	  else \
+	    col="$$red"; \
+	  fi; \
+	  echo "$${col}$$dashes$${std}"; \
+	  echo "$${col}$$banner$${std}"; \
+	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+	  test -z "$$report" || echo "$${col}$$report$${std}"; \
+	  echo "$${col}$$dashes$${std}"; \
+	  test "$$failed" -eq 0; \
+	else :; fi
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \
+	clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \
+	ctags ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am
+
+
+@GNOME_CODE_COVERAGE_RULES@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c
new file mode 100644
index 00000000..e97ef138
--- /dev/null
+++ b/src/platform/tests/dump.c
@@ -0,0 +1,136 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+#include "nm-platform.h"
+#include "nm-linux-platform.h"
+#include "nm-fake-platform.h"
+
+static void
+dump_interface (NMPlatformLink *link)
+{
+	GArray *ip6_addresses;
+	GArray *ip4_addresses;
+	const NMPlatformIP6Address *ip6_address;
+	const NMPlatformIP4Address *ip4_address;
+	GArray *ip6_routes;
+	GArray *ip4_routes;
+	const NMPlatformIP6Route *ip6_route;
+	const NMPlatformIP4Route *ip4_route;
+	char networkstr[INET6_ADDRSTRLEN];
+	char gatewaystr[INET6_ADDRSTRLEN];
+	int vlan_id, vlan_parent;
+	const char *address;
+	size_t addrlen;
+	int i;
+
+	g_assert (link->up || !link->connected);
+
+	printf ("%d: %s: %s", link->ifindex, link->name, link->type_name);
+	if (link->up)
+		printf (" %s", link->connected ? "CONNECTED" : "DISCONNECTED");
+	else
+		printf (" DOWN");
+	if (!link->arp)
+		printf (" noarp");
+	if (link->master)
+		printf (" master %d", link->master);
+	if (link->parent)
+		printf (" parent %d", link->parent);
+	printf (" mtu %d", link->mtu);
+	printf ("\n");
+	if (link->driver)
+		printf ("    driver: %s\n", link->driver);
+	printf ("    UDI: %s\n", link->udi);
+	if (!nm_platform_vlan_get_info (link->ifindex, &vlan_parent, &vlan_id))
+		g_assert_not_reached ();
+	if (vlan_parent)
+		printf ("    vlan parent %d id %d\n", vlan_parent, vlan_id);
+
+	if (nm_platform_link_is_software (link->ifindex))
+		printf ("    class software\n");
+	if (nm_platform_link_supports_slaves (link->ifindex))
+		printf ("    class supports-slaves\n");
+	if (nm_platform_link_supports_carrier_detect (link->ifindex))
+		printf ("    feature carrier-detect\n");
+	if (nm_platform_link_supports_vlans (link->ifindex))
+		printf ("    feature vlans\n");
+
+	address = nm_platform_link_get_address (link->ifindex, &addrlen);
+	if (address) {
+		printf ("    link-address ");
+		for (i = 0; i < addrlen; i++)
+			printf ("%s%02hhx", i ? ":" : "", address[i]);
+		printf ("\n");
+	}
+
+	ip4_addresses = nm_platform_ip4_address_get_all (link->ifindex);
+	ip6_addresses = nm_platform_ip6_address_get_all (link->ifindex);
+
+	g_assert (ip4_addresses);
+	g_assert (ip6_addresses);
+
+	for (i = 0; i < ip4_addresses->len; i++) {
+		ip4_address = &g_array_index (ip4_addresses, NMPlatformIP4Address, i);
+		printf ("    ip4-address %s\n", nm_platform_ip4_address_to_string (ip4_address));
+	}
+
+	for (i = 0; i < ip6_addresses->len; i++) {
+		ip6_address = &g_array_index (ip6_addresses, NMPlatformIP6Address, i);
+		printf ("    ip6-address %s\n", nm_platform_ip6_address_to_string (ip6_address));
+	}
+
+	g_array_unref (ip4_addresses);
+	g_array_unref (ip6_addresses);
+
+	ip4_routes = nm_platform_ip4_route_get_all (link->ifindex, TRUE);
+	ip6_routes = nm_platform_ip6_route_get_all (link->ifindex, TRUE);
+
+	g_assert (ip4_routes);
+	g_assert (ip6_routes);
+
+	for (i = 0; i < ip4_routes->len; i++) {
+		ip4_route = &g_array_index (ip4_routes, NMPlatformIP4Route, i);
+		inet_ntop (AF_INET, &ip4_route->network, networkstr, sizeof (networkstr));
+		inet_ntop (AF_INET, &ip4_route->gateway, gatewaystr, sizeof (gatewaystr));
+		printf ("    ip4-route %s/%d via %s\n", networkstr, ip4_route->plen, gatewaystr);
+	}
+
+	for (i = 0; i < ip6_routes->len; i++) {
+		ip6_route = &g_array_index (ip6_routes, NMPlatformIP6Route, i);
+		inet_ntop (AF_INET6, &ip6_route->network, networkstr, sizeof (networkstr));
+		inet_ntop (AF_INET6, &ip6_route->gateway, gatewaystr, sizeof (gatewaystr));
+		printf ("    ip6-route %s/%d via %s\n", networkstr, ip6_route->plen, gatewaystr);
+	}
+
+	g_array_unref (ip4_routes);
+	g_array_unref (ip6_routes);
+}
+
+static void
+dump_all (void)
+{
+	GArray *links = nm_platform_link_get_all ();
+	int i;
+
+	for (i = 0; i < links->len; i++)
+		dump_interface (&g_array_index (links, NMPlatformLink, i));
+}
+
+int
+main (int argc, char **argv)
+{
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+	g_type_init ();
+#endif
+
+	g_assert (argc <= 2);
+	if (argc > 1 && !g_strcmp0 (argv[1], "--fake"))
+		nm_fake_platform_setup ();
+	else
+		nm_linux_platform_setup ();
+
+	dump_all ();
+
+	return EXIT_SUCCESS;
+}
diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c
new file mode 100644
index 00000000..d56cc2bf
--- /dev/null
+++ b/src/platform/tests/monitor.c
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "nm-fake-platform.h"
+#include "nm-linux-platform.h"
+#include "nm-logging.h"
+
+int
+main (int argc, char **argv)
+{
+	GMainLoop *loop;
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+	g_type_init ();
+#endif
+
+	loop = g_main_loop_new (NULL, FALSE);
+	nm_logging_setup ("debug", NULL, NULL, NULL);
+	openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON);
+
+	g_assert (argc <= 2);
+	if (argc > 1 && !g_strcmp0 (argv[1], "--fake"))
+		nm_fake_platform_setup ();
+	else
+		nm_linux_platform_setup ();
+
+	g_main_loop_run (loop);
+
+	return EXIT_SUCCESS;
+}
diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c
new file mode 100644
index 00000000..ea8bd0e3
--- /dev/null
+++ b/src/platform/tests/platform.c
@@ -0,0 +1,897 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-platform.c - Handle runtime kernel networking configuration
+ *
+ * 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, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <netlink/route/addr.h>
+
+#include "nm-platform.h"
+#include "nm-linux-platform.h"
+#include "nm-fake-platform.h"
+
+#define error(...) fprintf (stderr, __VA_ARGS__)
+
+typedef gboolean boolean_t;
+typedef int decimal_t;
+typedef const char *string_t;
+
+#define print_boolean(value) printf ("%s\n", value ? "yes" : "no")
+#define print_decimal(value) printf ("%d\n", value)
+#define print_string(value) printf ("%s\n", value)
+
+static gboolean
+do_sysctl_set (char **argv)
+{
+	return nm_platform_sysctl_set (argv[0], argv[1]);
+}
+
+static gboolean
+do_sysctl_get (char **argv)
+{
+	auto_g_free char *value = nm_platform_sysctl_get (argv[0]);
+
+	printf ("%s\n", value);
+
+	return !!value;
+}
+
+static int
+parse_ifindex (const char *str)
+{
+	char *endptr;
+	int ifindex = 0;
+
+	ifindex = strtol (str, &endptr, 10);
+
+	if (*endptr) {
+		ifindex = nm_platform_link_get_ifindex (str);
+	}
+
+	return ifindex;
+}
+
+static gboolean
+do_link_get_all (char **argv)
+{
+	GArray *links;
+	NMPlatformLink *device;
+	int i;
+
+	links = nm_platform_link_get_all ();
+	for (i = 0; i < links->len; i++) {
+		device = &g_array_index (links, NMPlatformLink, i);
+
+		printf ("%d: %s type %d\n", device->ifindex, device->name, device->type);
+	}
+	g_array_unref (links);
+
+	return TRUE;
+}
+
+static gboolean
+do_dummy_add (char **argv)
+{
+	return nm_platform_dummy_add (argv[0]);
+}
+
+static gboolean
+do_bridge_add (char **argv)
+{
+	return nm_platform_bridge_add (argv[0], NULL, 0);
+}
+
+static gboolean
+do_bond_add (char **argv)
+{
+	return nm_platform_bond_add (argv[0]);
+}
+
+static gboolean
+do_team_add (char **argv)
+{
+	return nm_platform_team_add (argv[0]);
+}
+
+static gboolean
+do_vlan_add (char **argv)
+{
+	const char *name = *argv++;
+	int parent = parse_ifindex (*argv++);
+	int vlanid = strtol (*argv++, NULL, 10);
+	guint32 vlan_flags = strtol (*argv++, NULL, 10);
+
+	return nm_platform_vlan_add (name, parent, vlanid, vlan_flags);
+}
+
+static gboolean
+do_link_exists (char **argv)
+{
+	gboolean value = nm_platform_link_exists (argv[0]);
+
+	print_boolean (value);
+
+	return TRUE;
+}
+
+#define LINK_CMD(cmdname) \
+	static gboolean \
+	do_link_##cmdname (char **argv) \
+	{ \
+		int ifindex = parse_ifindex (argv[0]); \
+		return ifindex ? nm_platform_link_##cmdname (ifindex) : FALSE; \
+	}
+
+#define LINK_CMD_GET_FULL(cmdname, type, cond) \
+	static gboolean \
+	do_link_##cmdname (char **argv) \
+	{ \
+		int ifindex = parse_ifindex (argv[0]); \
+		if (ifindex) { \
+			type##_t value = nm_platform_link_##cmdname (ifindex); \
+			if (cond) { \
+				print_##type (value); \
+				return TRUE; \
+			} \
+		} \
+		return FALSE; \
+	}
+#define LINK_CMD_GET(cmdname, type) LINK_CMD_GET_FULL (cmdname, type, TRUE);
+
+LINK_CMD (delete)
+
+/* do_link_delete_by_ifname:
+ *
+ * We don't need this as we allow ifname instead of ifindex anyway.
+ */
+
+static gboolean
+do_link_get_ifindex (char **argv)
+{
+	int ifindex = nm_platform_link_get_ifindex (argv[0]);
+
+	if (ifindex)
+		printf ("%d\n", ifindex);
+
+	return !!ifindex;
+}
+
+LINK_CMD_GET_FULL (get_name, string, value)
+LINK_CMD_GET_FULL (get_type, decimal, value > 0)
+LINK_CMD_GET (is_software, boolean)
+LINK_CMD_GET (supports_slaves, boolean)
+
+LINK_CMD (set_up)
+LINK_CMD (set_down)
+LINK_CMD (set_arp)
+LINK_CMD (set_noarp)
+LINK_CMD_GET (is_up, boolean)
+LINK_CMD_GET (is_connected, boolean)
+LINK_CMD_GET (uses_arp, boolean)
+
+static gboolean
+do_link_set_address (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	char *hex = *argv++;
+	int hexlen = strlen (hex);
+	char address[hexlen/2];
+	char *endptr;
+	int i;
+
+	g_assert (!(hexlen % 2));
+
+	for (i = 0; i < sizeof (address); i++) {
+		char digit[3];
+
+		digit[0] = hex[2*i];
+		digit[1] = hex[2*i+1];
+		digit[2] = '\0';
+
+		address[i] = strtoul (digit, &endptr, 16);
+		g_assert (!*endptr);
+	}
+
+	return nm_platform_link_set_address (ifindex, address, sizeof (address));
+}
+
+static gboolean
+do_link_get_address (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	const char *address;
+	size_t length;
+	int i;
+
+	address = nm_platform_link_get_address (ifindex, &length);
+
+	if (!address || length <= 0)
+		return FALSE;
+
+	for (i = 0; i < length; i++)
+		printf ("%02x", address[i]);
+	printf ("\n");
+
+	return TRUE;
+}
+
+static gboolean
+do_link_set_mtu (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	int mtu = strtoul (*argv++, NULL, 10);
+
+	return nm_platform_link_set_mtu (ifindex, mtu);
+}
+
+LINK_CMD_GET (get_mtu, decimal);
+LINK_CMD_GET (supports_carrier_detect, boolean)
+LINK_CMD_GET (supports_vlans, boolean)
+
+static gboolean
+do_link_enslave (char **argv)
+{
+	int master = parse_ifindex (*argv++);
+	int slave = parse_ifindex (*argv++);
+
+	return nm_platform_link_enslave (master, slave);
+}
+
+static gboolean
+do_link_release (char **argv)
+{
+	int master = parse_ifindex (*argv++);
+	int slave = parse_ifindex (*argv++);
+
+	return nm_platform_link_release (master, slave);
+}
+
+LINK_CMD_GET (get_master, decimal)
+
+static gboolean
+do_master_set_option (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	const char *option = *argv++;
+	const char *value = *argv++;
+
+	return nm_platform_master_set_option (ifindex, option, value);
+}
+
+static gboolean
+do_master_get_option (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	const char *option = *argv++;
+	auto_g_free char *value = nm_platform_master_get_option (ifindex, option);
+
+	printf ("%s\n", value);
+
+	return !!value;
+}
+
+static gboolean
+do_slave_set_option (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	const char *option = *argv++;
+	const char *value = *argv++;
+
+	return nm_platform_slave_set_option (ifindex, option, value);
+}
+
+static gboolean
+do_slave_get_option (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	const char *option = *argv++;
+	auto_g_free char *value = nm_platform_slave_get_option (ifindex, option);
+
+	printf ("%s\n", value);
+
+	return !!value;
+}
+
+static gboolean
+do_vlan_get_info (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	int parent;
+	int vlanid;
+
+	if (!nm_platform_vlan_get_info (ifindex, &parent, &vlanid))
+		return FALSE;
+
+	printf ("%d %d\n", parent, vlanid);
+
+	return TRUE;
+}
+
+static gboolean
+do_vlan_set_ingress_map (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	int from = strtol (*argv++, NULL, 10);
+	int to = strtol (*argv++, NULL, 10);
+
+	return nm_platform_vlan_set_ingress_map (ifindex, from, to);
+}
+
+static gboolean
+do_vlan_set_egress_map (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	int from = strtol (*argv++, NULL, 10);
+	int to = strtol (*argv++, NULL, 10);
+
+	return nm_platform_vlan_set_egress_map (ifindex, from, to);
+}
+
+static gboolean
+do_veth_get_properties (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	NMPlatformVethProperties props;
+
+	if (!nm_platform_veth_get_properties (ifindex, &props))
+		return FALSE;
+
+	printf ("peer: %d\n", props.peer);
+
+	return TRUE;
+}
+
+static gboolean
+do_tun_get_properties (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	NMPlatformTunProperties props;
+
+	if (!nm_platform_tun_get_properties (ifindex, &props))
+		return FALSE;
+
+	printf ("mode: %s\n", props.mode);
+	if (props.owner == -1)
+		printf ("owner: none\n");
+	else
+		printf ("owner: %lu\n", (gulong) props.owner);
+	if (props.group == -1)
+		printf ("group: none\n");
+	else
+		printf ("group: %lu\n", (gulong) props.group);
+	printf ("no-pi: ");
+	print_boolean (props.no_pi);
+	printf ("vnet-hdr: ");
+	print_boolean (props.vnet_hdr);
+	printf ("multi-queue: ");
+	print_boolean (props.multi_queue);
+
+	return TRUE;
+}
+
+static gboolean
+do_macvlan_get_properties (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	NMPlatformMacvlanProperties props;
+
+	if (!nm_platform_macvlan_get_properties (ifindex, &props))
+		return FALSE;
+
+	printf ("parent: %d\n", props.parent_ifindex);
+	printf ("mode: %s\n", props.mode);
+	printf ("no-promisc: ");
+	print_boolean (props.no_promisc);
+	return TRUE;
+}
+
+static gboolean
+do_vxlan_get_properties (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	NMPlatformVxlanProperties props;
+	char addrstr[INET6_ADDRSTRLEN];
+
+	if (!nm_platform_vxlan_get_properties (ifindex, &props))
+		return FALSE;
+
+	printf ("parent-ifindex: %u\n", props.parent_ifindex);
+	printf ("id: %u\n", props.id);
+	if (props.group)
+		inet_ntop (AF_INET, &props.group, addrstr, sizeof (addrstr));
+	else if (props.group6.s6_addr[0])
+		inet_ntop (AF_INET6, &props.group6, addrstr, sizeof (addrstr));
+	else
+		strcpy (addrstr, "-");
+	printf ("group: %s\n", addrstr);
+	if (props.local)
+		inet_ntop (AF_INET, &props.local, addrstr, sizeof (addrstr));
+	else if (props.local6.s6_addr[0])
+		inet_ntop (AF_INET6, &props.local6, addrstr, sizeof (addrstr));
+	else
+		strcpy (addrstr, "-");
+	printf ("local: %s\n", addrstr);
+	printf ("tos: %u\n", props.tos);
+	printf ("ttl: %u\n", props.ttl);
+	printf ("learning: ");
+	print_boolean (props.learning);
+	printf ("ageing: %u\n", props.ageing);
+	printf ("limit: %u\n", props.limit);
+	printf ("dst-port: %u\n", props.dst_port);
+	printf ("src-port-min: %u\n", props.src_port_min);
+	printf ("src-port-max: %u\n", props.src_port_max);
+	printf ("proxy: ");
+	print_boolean (props.proxy);
+	printf ("rsc: ");
+	print_boolean (props.rsc);
+	printf ("l2miss: ");
+	print_boolean (props.l2miss);
+	printf ("l3miss: ");
+	print_boolean (props.l3miss);
+
+	return TRUE;
+}
+
+static gboolean
+do_gre_get_properties (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	NMPlatformGreProperties props;
+	char addrstr[INET_ADDRSTRLEN];
+
+	if (!nm_platform_gre_get_properties (ifindex, &props))
+		return FALSE;
+
+	printf ("parent-ifindex: %u\n", props.parent_ifindex);
+	printf ("input-flags: %u\n", props.input_flags);
+	printf ("output-flags: %u\n", props.input_flags);
+	printf ("input-key: %u\n", props.input_key);
+	printf ("output-key: %u\n", props.output_key);
+	if (props.local)
+		inet_ntop (AF_INET, &props.local, addrstr, sizeof (addrstr));
+	else
+		strcpy (addrstr, "-");
+	printf ("local: %s\n", addrstr);
+	if (props.remote)
+		inet_ntop (AF_INET, &props.remote, addrstr, sizeof (addrstr));
+	else
+		strcpy (addrstr, "-");
+	printf ("remote: %s\n", addrstr);
+	printf ("ttl: %u\n", props.ttl);
+	printf ("tos: %u\n", props.tos);
+	printf ("path-mtu-discovery: ");
+	print_boolean (props.path_mtu_discovery);
+
+	return TRUE;
+}
+
+static gboolean
+do_ip4_address_get_all (char **argv)
+{
+	int ifindex = parse_ifindex (argv[0]);
+	GArray *addresses;
+	NMPlatformIP4Address *address;
+	char addrstr[INET_ADDRSTRLEN];
+	int i;
+
+	if (ifindex) {
+		addresses = nm_platform_ip4_address_get_all (ifindex);
+		for (i = 0; i < addresses->len; i++) {
+			address = &g_array_index (addresses, NMPlatformIP4Address, i);
+			inet_ntop (AF_INET, &address->address, addrstr, sizeof (addrstr));
+			printf ("%s/%d\n", addrstr, address->plen);
+		}
+		g_array_unref (addresses);
+	}
+
+	return !!ifindex;
+}
+
+static gboolean
+do_ip6_address_get_all (char **argv)
+{
+	int ifindex = parse_ifindex (argv[0]);
+	GArray *addresses;
+	NMPlatformIP6Address *address;
+	char addrstr[INET6_ADDRSTRLEN];
+	int i;
+
+	if (ifindex) {
+		addresses = nm_platform_ip6_address_get_all (ifindex);
+		for (i = 0; i < addresses->len; i++) {
+			address = &g_array_index (addresses, NMPlatformIP6Address, i);
+			inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr));
+			printf ("%s/%d\n", addrstr, address->plen);
+		}
+		g_array_unref (addresses);
+	}
+
+	return !!ifindex;
+}
+
+static gboolean
+parse_ip_address (int family, char *str, gpointer address, int *plen)
+{
+	char *endptr;
+
+	if (plen) {
+		char *ptr = strchr (str, '/');
+		if (ptr) {
+			*ptr++ = '\0';
+			*plen = strtol (ptr, &endptr, 10);
+			if (*endptr)
+				ptr = NULL;
+		}
+		if (!ptr) {
+			error ("Bad format of IP address, expected address/plen.\n");
+			return FALSE;
+		}
+	}
+
+	if (inet_pton (family, str, address))
+		return TRUE;
+
+	error ("Bad format of IP address, expected address%s.\n", plen ? "/plen" : "");
+	return FALSE;
+}
+
+typedef in_addr_t ip4_t;
+typedef struct in6_addr ip6_t;
+
+#define parse_ip4_address(s, a, p) parse_ip_address (AF_INET, s, a, p)
+#define parse_ip6_address(s, a, p) parse_ip_address (AF_INET6, s, a, p)
+
+static gboolean
+do_ip4_address_add (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	ip4_t address;
+	int plen;
+
+	if (ifindex && parse_ip4_address (*argv++, &address, &plen)) {
+		guint32 lifetime = strtol (*argv++, NULL, 10);
+		guint32 preferred = strtol (*argv++, NULL, 10);
+
+		gboolean value = nm_platform_ip4_address_add (ifindex, address, 0, plen, lifetime, preferred, NULL);
+		return value;
+	} else
+		return FALSE;
+}
+
+static gboolean
+do_ip6_address_add (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	ip6_t address;
+	int plen;
+
+	if (ifindex && parse_ip6_address (*argv++, &address, &plen)) {
+		guint32 lifetime = strtol (*argv++, NULL, 10);
+		guint32 preferred = strtol (*argv++, NULL, 10);
+		guint flags = (*argv) ? rtnl_addr_str2flags (*argv++) : 0;
+
+		gboolean value = nm_platform_ip6_address_add (ifindex, address, in6addr_any, plen, lifetime, preferred, flags);
+		return value;
+	} else
+		return FALSE;
+}
+
+#define ADDR_CMD_FULL(v, cmdname, print) \
+	static gboolean \
+	do_##v##_address_##cmdname (char **argv) \
+	{ \
+		int ifindex = parse_ifindex (*argv++); \
+		v##_t address; \
+		int plen; \
+		if (ifindex && parse_##v##_address (*argv++, &address, &plen)) { \
+			gboolean value = nm_platform_##v##_address_##cmdname (ifindex, address, plen); \
+			if (print) { \
+				print_boolean (value); \
+				return TRUE; \
+			} else \
+				return value; \
+		} else \
+			return FALSE; \
+	}
+#define ADDR_CMD(cmdname) ADDR_CMD_FULL (ip4, cmdname, FALSE) ADDR_CMD_FULL (ip6, cmdname, FALSE)
+#define ADDR_CMD_PRINT(cmdname) ADDR_CMD_FULL (ip4, cmdname, TRUE) ADDR_CMD_FULL (ip6, cmdname, TRUE)
+
+ADDR_CMD (delete)
+ADDR_CMD_PRINT (exists)
+
+static gboolean
+do_ip4_route_get_all (char **argv)
+{
+	int ifindex = parse_ifindex (argv[0]);
+	GArray *routes;
+	NMPlatformIP4Route *route;
+	char networkstr[INET_ADDRSTRLEN], gatewaystr[INET_ADDRSTRLEN];
+	int i;
+
+	if (ifindex) {
+		routes = nm_platform_ip4_route_get_all (ifindex, TRUE);
+		for (i = 0; i < routes->len; i++) {
+			route = &g_array_index (routes, NMPlatformIP4Route, i);
+			inet_ntop (AF_INET, &route->network, networkstr, sizeof (networkstr));
+			inet_ntop (AF_INET, &route->gateway, gatewaystr, sizeof (gatewaystr));
+			printf ("%s/%d via %s metric %d\n",
+					networkstr, route->plen, gatewaystr, route->metric);
+		}
+		g_array_unref (routes);
+	}
+
+	return !!ifindex;
+}
+
+static gboolean
+do_ip6_route_get_all (char **argv)
+{
+	int ifindex = parse_ifindex (argv[0]);
+	GArray *routes;
+	NMPlatformIP6Route *route;
+	char networkstr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
+	int i;
+
+	if (ifindex) {
+		routes = nm_platform_ip6_route_get_all (ifindex, TRUE);
+		for (i = 0; i < routes->len; i++) {
+			route = &g_array_index (routes, NMPlatformIP6Route, i);
+			inet_ntop (AF_INET6, &route->network, networkstr, sizeof (networkstr));
+			inet_ntop (AF_INET6, &route->gateway, gatewaystr, sizeof (gatewaystr));
+			printf ("%s/%d via %s metric %d\n",
+					networkstr, route->plen, gatewaystr, route->metric);
+		}
+		g_array_unref (routes);
+	}
+
+	return !!ifindex;
+}
+
+static gboolean
+do_ip4_route_add (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	in_addr_t network, gateway;
+	int plen, metric, mss;
+
+	parse_ip4_address (*argv++, &network, &plen);
+	parse_ip4_address (*argv++, &gateway, NULL);
+	metric = strtol (*argv++, NULL, 10);
+	mss = strtol (*argv++, NULL, 10);
+
+	return nm_platform_ip4_route_add (ifindex, NM_PLATFORM_SOURCE_USER,
+	                                  network, plen, gateway,
+	                                  metric, mss);
+}
+
+static gboolean
+do_ip6_route_add (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	struct in6_addr network, gateway;
+	int plen, metric, mss;
+
+	parse_ip6_address (*argv++, &network, &plen);
+	parse_ip6_address (*argv++, &gateway, NULL);
+	metric = strtol (*argv++, NULL, 10);
+	mss = strtol (*argv++, NULL, 10);
+	return nm_platform_ip6_route_add (ifindex, NM_PLATFORM_SOURCE_USER,
+	                                  network, plen, gateway,
+	                                  metric, mss);
+}
+
+static gboolean
+do_ip4_route_delete (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	in_addr_t network;
+	int plen, metric;
+
+	parse_ip4_address (*argv++, &network, &plen);
+	metric = strtol (*argv++, NULL, 10);
+
+	return nm_platform_ip4_route_delete (ifindex, network, plen, metric);
+}
+
+static gboolean
+do_ip6_route_delete (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	struct in6_addr network;
+	int plen, metric;
+
+	parse_ip6_address (*argv++, &network, &plen);
+	metric = strtol (*argv++, NULL, 10);
+
+	return nm_platform_ip6_route_delete (ifindex, network, plen, metric);
+}
+
+static gboolean
+do_ip4_route_exists (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	in_addr_t network;
+	int plen, metric;
+
+	parse_ip4_address (*argv++, &network, &plen);
+	metric = strtol (*argv++, NULL, 10);
+
+	print_boolean (nm_platform_ip4_route_exists (ifindex, network, plen, metric));
+	return TRUE;
+}
+
+static gboolean
+do_ip6_route_exists (char **argv)
+{
+	int ifindex = parse_ifindex (*argv++);
+	struct in6_addr network;
+	int plen, metric;
+
+	parse_ip6_address (*argv++, &network, &plen);
+	metric = strtol (*argv++, NULL, 10);
+
+	print_boolean (nm_platform_ip6_route_exists (ifindex, network, plen, metric));
+	return TRUE;
+}
+
+typedef struct {
+	const char *name;
+	const char *help;
+	int (*handler) (char **argv);
+	int argc;
+	const char *arghelp;
+} command_t;
+
+static const command_t commands[] = {
+	{ "sysctl-set", "get /proc/sys or /sys value", do_sysctl_set, 2, "<path> <value>" },
+	{ "sysctl-get", "get /proc/sys or /sys value", do_sysctl_get, 1, "<value>" },
+	{ "link-get-all", "print all links", do_link_get_all, 0, "" },
+	{ "dummy-add", "add dummy interface", do_dummy_add, 1, "<ifname>" },
+	{ "bridge-add", "add bridge interface", do_bridge_add, 1, "<ifname>" },
+	{ "bond-add", "add bond interface", do_bond_add, 1, "<ifname>" },
+	{ "team-add", "add team interface", do_team_add, 1, "<ifname>" },
+	{ "vlan-add", "add vlan interface", do_vlan_add, 4, "<ifname> <parent> <vlanid> <vlanflags>" },
+	{ "link-exists", "check ifname for existance", do_link_exists, 1, "<ifname>" },
+	{ "link-delete", "delete interface", do_link_delete, 1, "<ifname/ifindex>" },
+	{ "link-get-ifindex>", "get interface index", do_link_get_ifindex, 1, "<ifname>" },
+	{ "link-get-name", "get interface name", do_link_get_name, 1, "<ifindex>" },
+	{ "link-get-type", "get interface type", do_link_get_type, 1, "<ifname/ifindex>" },
+	{ "link-is-software", "check if interface is a software one", do_link_is_software, 1, "<ifname/ifindex>" },
+	{ "link-supports-slaves", "check if interface supports slaves", do_link_supports_slaves, 1, "<ifname/ifindex>" },
+	{ "link-set-up", "set interface up", do_link_set_up, 1, "<ifname/ifindex>" },
+	{ "link-set-down", "set interface down", do_link_set_down, 1, "<ifname/ifindex>" },
+	{ "link-set-arp", "activate interface arp", do_link_set_arp, 1, "<ifname/ifindex>" },
+	{ "link-set-noarp", "deactivate interface arp", do_link_set_noarp, 1, "<ifname/ifindex>" },
+	{ "link-is-up", "check if interface is up", do_link_is_up, 1, "<ifname/ifindex>" },
+	{ "link-is-connected", "check interface carrier", do_link_is_connected, 1, "<ifname/ifindex>" },
+	{ "link-uses-arp", "check whether interface uses arp", do_link_uses_arp, 1, "<ifname/ifindex>" },
+	{ "link-get-address", "print link address", do_link_get_address, 1, "<ifname/ifindex>" },
+	{ "link-set-address", "set link address", do_link_set_address, 2, "<ifname/ifindex> <hex>" },
+	{ "link-get-mtu", "print link mtu", do_link_get_mtu, 1, "<ifname/ifindex>" },
+	{ "link-set-mtu", "set link mtu", do_link_set_mtu, 2, "<ifname/ifindex> <mtu>" },
+	{ "link-supports-carrier-detect", "check whether interface supports carrier detect",
+		do_link_supports_carrier_detect, 1, "<ifname/ifindex>" },
+	{ "link-supports-vlans", "check whether interface supports VLANs",
+		do_link_supports_vlans, 1, "<ifname/ifindex>" },
+	{ "link-enslave", "enslave slave interface with master", do_link_enslave, 2, "<master> <slave>" },
+	{ "link-release", "release save interface from master", do_link_release, 2, "<master> <slave>" },
+	{ "link-get-master", "print master interface of a slave", do_link_get_master, 1, "<ifname/ifindex>" },
+	{ "link-master-set-option", "set master option", do_master_set_option, 3,
+		"<ifname/ifindex> <option> <value>" },
+	{ "link-master-get-option", "get master option", do_master_get_option, 2,
+		"<ifname/ifindex> <option>" },
+	{ "link-slave-set-option", "set slave option", do_slave_set_option, 3,
+		"<ifname/ifindex> <option>" },
+	{ "link-slave-get-option", "get slave option", do_slave_get_option, 2,
+		"<ifname/ifindex> <option>" },
+	{ "vlan-get-info", "get vlan info", do_vlan_get_info, 1, "<ifname/ifindex>" },
+	{ "vlan-set-ingress-map", "set vlan ingress map", do_vlan_set_ingress_map, 3,
+		"<ifname/ifindex> <from> <to>" },
+	{ "vlan-set-egress-map", "set vlan egress map", do_vlan_set_egress_map, 3,
+		"<ifname/ifindex> <from> <to>" },
+	{ "veth-get-properties", "get veth properties", do_veth_get_properties, 1,
+	  "<ifname/ifindex>" },
+	{ "tun-get-properties", "get tun/tap properties", do_tun_get_properties, 1,
+	  "<ifname/ifindex>" },
+	{ "macvlan-get-properties", "get macvlan properties", do_macvlan_get_properties, 1,
+	  "<ifname/ifindex>" },
+	{ "vxlan-get-properties", "get vxlan properties", do_vxlan_get_properties, 1,
+	  "<ifname/ifindex>" },
+	{ "gre-get-properties", "get gre properties", do_gre_get_properties, 1,
+	  "<ifname/ifindex>" },
+	{ "ip4-address-get-all", "print all IPv4 addresses", do_ip4_address_get_all, 1, "<ifname/ifindex>" },
+	{ "ip6-address-get-all", "print all IPv6 addresses", do_ip6_address_get_all, 1, "<ifname/ifindex>" },
+	{ "ip4-address-add", "add IPv4 address", do_ip4_address_add, 4, "<ifname/ifindex> <address>/<plen> <lifetime> <>" },
+	{ "ip6-address-add", "add IPv6 address", do_ip6_address_add, 4, "<ifname/ifindex> <address>/<plen> <lifetime> [<flags>] <>" },
+	{ "ip4-address-delete", "delete IPv4 address", do_ip4_address_delete, 2,
+		"<ifname/ifindex> <address>/<plen>" },
+	{ "ip6-address-delete", "delete IPv6 address", do_ip6_address_delete, 2,
+		"<ifname/ifindex> <address>/<plen>" },
+	{ "ip4-address-exists", "check for existence of IPv4 address", do_ip4_address_exists, 2,
+		"<ifname/ifindex> <address>/<plen>" },
+	{ "ip6-address-exists", "check for existence of IPv6 address", do_ip6_address_exists, 2,
+		"<ifname/ifindex> <address>/<plen>" },
+	{ "ip4-route-get-all", "print all IPv4 routes", do_ip4_route_get_all, 1, "<ifname/ifindex>" },
+	{ "ip6-route-get-all", "print all IPv6 routes", do_ip6_route_get_all, 1, "<ifname/ifindex>" },
+	{ "ip4-route-add", "add IPv4 route", do_ip4_route_add, 5,
+		"<ifname/ifindex> <network>/<plen> <gateway> <metric> <mss>" },
+	{ "ip6-route-add", "add IPv6 route", do_ip6_route_add, 5,
+		"<ifname/ifindex> <network>/<plen> <gateway> <metric> <mss>" },
+	{ "ip4-route-delete", "delete IPv4 route", do_ip4_route_delete, 3,
+		"<ifname/ifindex> <network>/<plen> <metric>" },
+	{ "ip6-route-delete", "delete IPv6 route", do_ip6_route_delete, 3,
+		"<ifname/ifindex> <network>/<plen> <metric>" },
+	{ "ip4-route-exists", "check for existence of IPv4 route", do_ip4_route_exists, 3,
+		"<ifname/ifindex> <network>/<plen> <metric>" },
+	{ "ip6-route-exists", "check for existence of IPv6 route", do_ip6_route_exists, 3,
+		"<ifname/ifindex> <network>/<plen> <metric>" },
+	{ NULL, NULL, NULL, 0, NULL },
+};
+
+int
+main (int argc, char **argv)
+{
+	const char *arg0 = *argv++;
+	const command_t *command = NULL;
+	gboolean status = TRUE;
+	int error;
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+	g_type_init ();
+#endif
+
+	if (*argv && !g_strcmp0 (argv[1], "--fake")) {
+		nm_fake_platform_setup ();
+	} else
+		nm_linux_platform_setup ();
+
+	if (*argv)
+		for (command = commands; command->name; command++)
+			if (g_str_has_prefix (command->name, *argv))
+				break;
+
+	if (command && command->name) {
+		argv++;
+		if (g_strv_length (argv) == command->argc)
+			status = command->handler (argv);
+		else {
+			error ("Wrong number of arguments to '%s' (expected %d).\n\nUsage: %s %s %s\n-- %s\n",
+					command->name, command->argc,
+					arg0, command->name, command->arghelp, command->help);
+			return EXIT_FAILURE;
+		}
+	} else {
+		error ("Usage: %s COMMAND\n\n", arg0);
+		error ("COMMAND\n");
+		for (command = commands; command->name; command++)
+			error ("  %s %s\n    -- %s\n", command->name, command->arghelp, command->help);
+		error ("\n");
+	}
+
+	error = nm_platform_get_error ();
+	if (error) {
+		const char *msg = nm_platform_get_error_msg ();
+
+		error ("nm-platform: %s\n", msg);
+	}
+
+	return !!error;
+}
diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c
new file mode 100644
index 00000000..3fef5375
--- /dev/null
+++ b/src/platform/tests/test-address.c
@@ -0,0 +1,265 @@
+#include "test-common.h"
+
+#define DEVICE_NAME "nm-test-device"
+#define IP4_ADDRESS "192.0.2.1"
+#define IP4_PLEN 24
+#define IP6_ADDRESS "2001:db8:a:b:1:2:3:4"
+#define IP6_PLEN 64
+
+static void
+ip4_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
+{
+	g_assert (received);
+	g_assert_cmpint (received->ifindex, ==, ifindex);
+	g_assert (data && data->name);
+	g_assert_cmpstr (data->name, ==, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED);
+
+	if (data->ifindex && data->ifindex != received->ifindex)
+		return;
+	if (data->change_type != change_type)
+		return;
+
+	if (data->loop)
+		g_main_loop_quit (data->loop);
+
+	if (data->received)
+		g_error ("Received signal '%s' a second time.", data->name);
+
+	data->received = TRUE;
+}
+
+static void
+ip6_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
+{
+	g_assert (received);
+	g_assert_cmpint (received->ifindex, ==, ifindex);
+	g_assert (data && data->name);
+	g_assert_cmpstr (data->name, ==, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED);
+
+	if (data->ifindex && data->ifindex != received->ifindex)
+		return;
+	if (data->change_type != change_type)
+		return;
+
+	if (data->loop)
+		g_main_loop_quit (data->loop);
+
+	if (data->received)
+		g_error ("Received signal '%s' a second time.", data->name);
+
+	data->received = TRUE;
+}
+
+static void
+test_ip4_address (void)
+{
+	int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	SignalData *address_added = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_address_callback, ifindex);
+	SignalData *address_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_address_callback, ifindex);
+	SignalData *address_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_address_callback, ifindex);
+	GArray *addresses;
+	NMPlatformIP4Address *address;
+	in_addr_t addr;
+	guint32 lifetime = 2000;
+	guint32 preferred = 1000;
+
+	inet_pton (AF_INET, IP4_ADDRESS, &addr);
+
+	/* Add address */
+	g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN));
+	no_error ();
+	g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL));
+	no_error ();
+	g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN));
+	no_error ();
+	accept_signal (address_added);
+
+	/* Add address again (aka update) */
+	g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL));
+	no_error ();
+	accept_signal (address_changed);
+
+	/* Test address listing */
+	addresses = nm_platform_ip4_address_get_all (ifindex);
+	g_assert (addresses);
+	no_error ();
+	g_assert_cmpint (addresses->len, ==, 1);
+	address = &g_array_index (addresses, NMPlatformIP4Address, 0);
+	g_assert_cmpint (address->ifindex, ==, ifindex);
+	g_assert_cmphex (address->address, ==, addr);
+	g_assert_cmpint (address->plen, ==, IP4_PLEN);
+	g_array_unref (addresses);
+
+	/* Remove address */
+	g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN));
+	no_error ();
+	g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN));
+	accept_signal (address_removed);
+
+	/* Remove address again */
+	g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN));
+	no_error ();
+
+	free_signal (address_added);
+	free_signal (address_changed);
+	free_signal (address_removed);
+}
+
+static void
+test_ip6_address (void)
+{
+	int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	SignalData *address_added = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_address_callback, ifindex);
+	SignalData *address_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_address_callback, ifindex);
+	SignalData *address_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_address_callback, ifindex);
+	GArray *addresses;
+	NMPlatformIP6Address *address;
+	struct in6_addr addr;
+	guint32 lifetime = 2000;
+	guint32 preferred = 1000;
+	guint flags = 0;
+
+	inet_pton (AF_INET6, IP6_ADDRESS, &addr);
+
+	/* Add address */
+	g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN));
+	no_error ();
+	g_assert (nm_platform_ip6_address_add (ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags));
+	no_error ();
+	g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN));
+	no_error ();
+	accept_signal (address_added);
+
+	/* Add address again (aka update) */
+	g_assert (nm_platform_ip6_address_add (ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags));
+	no_error ();
+	accept_signal (address_changed);
+
+	/* Test address listing */
+	addresses = nm_platform_ip6_address_get_all (ifindex);
+	g_assert (addresses);
+	no_error ();
+	g_assert_cmpint (addresses->len, ==, 1);
+	address = &g_array_index (addresses, NMPlatformIP6Address, 0);
+	g_assert_cmpint (address->ifindex, ==, ifindex);
+	g_assert (!memcmp (&address->address, &addr, sizeof (addr)));
+	g_assert_cmpint (address->plen, ==, IP6_PLEN);
+	g_array_unref (addresses);
+
+	/* Remove address */
+	g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN));
+	no_error ();
+	g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN));
+	accept_signal (address_removed);
+
+	/* Remove address again */
+	g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN));
+	no_error ();
+
+	free_signal (address_added);
+	free_signal (address_changed);
+	free_signal (address_removed);
+}
+
+static void
+test_ip4_address_external (void)
+{
+	SignalData *address_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_address_callback);
+	SignalData *address_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_address_callback);
+	int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	in_addr_t addr;
+	guint32 lifetime = 2000;
+	guint32 preferred = 1000;
+
+	inet_pton (AF_INET, IP4_ADDRESS, &addr);
+	g_assert (ifindex > 0);
+
+	/* Looks like addresses are not announced by kerenl when the interface
+	 * is down. Link-local IPv6 address is automatically added.
+	 */
+	g_assert (nm_platform_link_set_up (nm_platform_link_get_ifindex (DEVICE_NAME)));
+
+	/* Add/delete notification */
+	run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d",
+			IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred);
+	wait_signal (address_added);
+	g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN));
+	run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME);
+	wait_signal (address_removed);
+	g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN));
+
+	/* Add/delete conflict */
+	run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d",
+			IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred);
+	g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL));
+	no_error ();
+	g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN));
+	accept_signal (address_added);
+	/*run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME);
+	g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN));
+	no_error ();
+	g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN));
+	accept_signal (address_removed);*/
+
+	free_signal (address_added);
+	free_signal (address_removed);
+}
+
+static void
+test_ip6_address_external (void)
+{
+	SignalData *address_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_address_callback);
+	SignalData *address_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_address_callback);
+	int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	struct in6_addr addr;
+	guint32 lifetime = 2000;
+	guint32 preferred = 1000;
+	guint flags = 0;
+
+	inet_pton (AF_INET6, IP6_ADDRESS, &addr);
+
+	/* Add/delete notification */
+	run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d",
+			IP6_ADDRESS, IP6_PLEN, DEVICE_NAME, lifetime, preferred);
+	wait_signal (address_added);
+	g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN));
+	run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME);
+	wait_signal (address_removed);
+	g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN));
+
+	/* Add/delete conflict */
+	run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d",
+			IP6_ADDRESS, IP6_PLEN, DEVICE_NAME, lifetime, preferred);
+	g_assert (nm_platform_ip6_address_add (ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags));
+	no_error ();
+	g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN));
+	accept_signal (address_added);
+	/*run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME);
+	g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN));
+	no_error ();
+	g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN));
+	wait_signal (address_removed);*/
+
+	free_signal (address_added);
+	free_signal (address_removed);
+}
+
+void
+setup_tests (void)
+{
+	SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME);
+
+	nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME));
+	g_assert (!nm_platform_link_exists (DEVICE_NAME));
+	g_assert (nm_platform_dummy_add (DEVICE_NAME));
+	accept_signal (link_added);
+	free_signal (link_added);
+
+	g_test_add_func ("/address/internal/ip4", test_ip4_address);
+	g_test_add_func ("/address/internal/ip6", test_ip6_address);
+
+	if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform")) {
+		g_test_add_func ("/address/external/ip4", test_ip4_address_external);
+		g_test_add_func ("/address/external/ip6", test_ip6_address_external);
+	}
+}
diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c
new file mode 100644
index 00000000..e60d82f0
--- /dev/null
+++ b/src/platform/tests/test-cleanup.c
@@ -0,0 +1,95 @@
+#include "test-common.h"
+
+#define DEVICE_NAME "nm-test-device"
+
+static void
+test_cleanup_internal ()
+{
+	SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME);
+	int ifindex;
+	GArray *addresses4;
+	GArray *addresses6;
+	GArray *routes4;
+	GArray *routes6;
+	in_addr_t addr4;
+	in_addr_t network4;
+	int plen4 = 24;
+	in_addr_t gateway4;
+	struct in6_addr addr6;
+	struct in6_addr network6;
+	int plen6 = 64;
+	struct in6_addr gateway6;
+	int lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
+	int preferred = NM_PLATFORM_LIFETIME_PERMANENT;
+	int metric = 20;
+	int mss = 1000;
+	guint flags = 0;
+
+	inet_pton (AF_INET, "192.0.2.1", &addr4);
+	inet_pton (AF_INET, "192.0.3.0", &network4);
+	inet_pton (AF_INET, "198.51.100.1", &gateway4);
+	inet_pton (AF_INET6, "2001:db8:a:b:1:2:3:4", &addr6);
+	inet_pton (AF_INET6, "2001:db8:c:d:0:0:0:0", &network6);
+	inet_pton (AF_INET6, "2001:db8:e:f:1:2:3:4", &gateway6);
+
+	/* Create and set up device */
+	g_assert (nm_platform_dummy_add (DEVICE_NAME));
+	accept_signal (link_added);
+	free_signal (link_added);
+	g_assert (nm_platform_link_set_up (nm_platform_link_get_ifindex (DEVICE_NAME)));
+	ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	g_assert (ifindex > 0);
+
+	/* Add routes and addresses */
+	g_assert (nm_platform_ip4_address_add (ifindex, addr4, 0, plen4, lifetime, preferred, NULL));
+	g_assert (nm_platform_ip6_address_add (ifindex, addr6, in6addr_any, plen6, lifetime, preferred, flags));
+	g_assert (nm_platform_ip4_route_add (ifindex, NM_PLATFORM_SOURCE_USER, gateway4, 32, INADDR_ANY, metric, mss));
+	g_assert (nm_platform_ip4_route_add (ifindex, NM_PLATFORM_SOURCE_USER, network4, plen4, gateway4, metric, mss));
+	g_assert (nm_platform_ip4_route_add (ifindex, NM_PLATFORM_SOURCE_USER, 0, 0, gateway4, metric, mss));
+	g_assert (nm_platform_ip6_route_add (ifindex, NM_PLATFORM_SOURCE_USER, gateway6, 128, in6addr_any, metric, mss));
+	g_assert (nm_platform_ip6_route_add (ifindex, NM_PLATFORM_SOURCE_USER, network6, plen6, gateway6, metric, mss));
+	g_assert (nm_platform_ip6_route_add (ifindex, NM_PLATFORM_SOURCE_USER, in6addr_any, 0, gateway6, metric, mss));
+
+	addresses4 = nm_platform_ip4_address_get_all (ifindex);
+	addresses6 = nm_platform_ip6_address_get_all (ifindex);
+	routes4 = nm_platform_ip4_route_get_all (ifindex, TRUE);
+	routes6 = nm_platform_ip6_route_get_all (ifindex, TRUE);
+
+	g_assert_cmpint (addresses4->len, ==, 1);
+	g_assert_cmpint (addresses6->len, ==, 1);
+	g_assert_cmpint (routes4->len, ==, 3);
+	g_assert_cmpint (routes6->len, ==, 3);
+
+	g_array_unref (addresses4);
+	g_array_unref (addresses6);
+	g_array_unref (routes4);
+	g_array_unref (routes6);
+
+	/* Delete interface with all addresses and routes */
+	g_assert (nm_platform_link_delete (ifindex));
+
+	addresses4 = nm_platform_ip4_address_get_all (ifindex);
+	addresses6 = nm_platform_ip6_address_get_all (ifindex);
+	routes4 = nm_platform_ip4_route_get_all (ifindex, TRUE);
+	routes6 = nm_platform_ip6_route_get_all (ifindex, TRUE);
+
+	g_assert_cmpint (addresses4->len, ==, 0);
+	g_assert_cmpint (addresses6->len, ==, 0);
+	g_assert_cmpint (routes4->len, ==, 0);
+	g_assert_cmpint (routes6->len, ==, 0);
+
+	g_array_unref (addresses4);
+	g_array_unref (addresses6);
+	g_array_unref (routes4);
+	g_array_unref (routes6);
+}
+
+void
+setup_tests (void)
+{
+	nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME));
+	g_assert (!nm_platform_link_exists (DEVICE_NAME));
+
+	g_test_add_func ("/internal", test_cleanup_internal);
+	/* FIXME: add external cleanup check */
+}
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
new file mode 100644
index 00000000..59a770c6
--- /dev/null
+++ b/src/platform/tests/test-common.c
@@ -0,0 +1,174 @@
+#include "test-common.h"
+
+#include "nm-test-utils.h"
+
+SignalData *
+add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname)
+{
+	SignalData *data = g_new0 (SignalData, 1);
+
+	data->name = name;
+	data->change_type = change_type;
+	data->received = FALSE;
+	data->handler_id = g_signal_connect (nm_platform_get (), name, callback, data);
+	data->ifindex = ifindex;
+	data->ifname = ifname;
+
+	g_assert (data->handler_id >= 0);
+
+	return data;
+}
+
+static const char *
+_change_type_to_string (NMPlatformSignalChangeType change_type)
+{
+    switch (change_type) {
+    case NM_PLATFORM_SIGNAL_ADDED:
+        return "added";
+    case NM_PLATFORM_SIGNAL_CHANGED:
+        return "changed";
+    case NM_PLATFORM_SIGNAL_REMOVED:
+        return "removed";
+    default:
+        g_return_val_if_reached ("UNKNOWN");
+    }
+}
+
+void
+accept_signal (SignalData *data)
+{
+	debug ("Accepting signal '%s-%s' ifindex %d ifname %s.", data->name, _change_type_to_string (data->change_type), data->ifindex, data->ifname);
+	if (!data->received)
+		g_error ("Attemted to accept a non-received signal '%s-%s'.", data->name, _change_type_to_string (data->change_type));
+
+	data->received = FALSE;
+}
+
+void
+wait_signal (SignalData *data)
+{
+	if (data->received)
+		g_error ("Signal '%s' received before waiting for it.", data->name);
+
+	data->loop = g_main_loop_new (NULL, FALSE);
+	g_main_loop_run (data->loop);
+	g_clear_pointer (&data->loop, g_main_loop_unref);
+
+	accept_signal (data);
+}
+
+void
+free_signal (SignalData *data)
+{
+	if (data->received)
+		g_error ("Attempted to free received but not accepted signal '%s-%s'.", data->name, _change_type_to_string (data->change_type));
+
+	g_signal_handler_disconnect (nm_platform_get (), data->handler_id);
+	g_free (data);
+}
+
+void
+link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
+{
+	
+	GArray *links;
+	NMPlatformLink *cached;
+	int i;
+
+	g_assert (received);
+	g_assert_cmpint (received->ifindex, ==, ifindex);
+	g_assert (data && data->name);
+	g_assert_cmpstr (data->name, ==, NM_PLATFORM_SIGNAL_LINK_CHANGED);
+
+	if (data->ifindex && data->ifindex != received->ifindex)
+		return;
+	if (data->ifname && g_strcmp0 (data->ifname, nm_platform_link_get_name (ifindex)) != 0)
+		return;
+	if (change_type != data->change_type)
+		return;
+
+	if (data->loop) {
+		debug ("Quitting main loop.");
+		g_main_loop_quit (data->loop);
+	}
+
+	if (data->received)
+		g_error ("Received signal '%s-%s' a second time.", data->name, _change_type_to_string (data->change_type));
+
+	debug ("Received signal '%s-%s' ifindex %d ifname '%s'.", data->name, _change_type_to_string (data->change_type), ifindex, received->name);
+	data->received = TRUE;
+
+	if (change_type == NM_PLATFORM_SIGNAL_REMOVED)
+		g_assert (!nm_platform_link_get_name (ifindex));
+	else
+		g_assert (nm_platform_link_get_name (ifindex));
+
+	/* Check the data */
+	g_assert (received->ifindex > 0);
+	links = nm_platform_link_get_all ();
+	for (i = 0; i < links->len; i++) {
+		cached = &g_array_index (links, NMPlatformLink, i);
+		if (cached->ifindex == received->ifindex) {
+			g_assert_cmpint (nm_platform_link_cmp (cached, received), ==, 0);
+			g_assert (!memcmp (cached, received, sizeof (*cached)));
+			if (data->change_type == NM_PLATFORM_SIGNAL_REMOVED)
+				g_error ("Deleted link still found in the local cache.");
+			g_array_unref (links);
+			return;
+		}
+	}
+	g_array_unref (links);
+
+	if (data->change_type != NM_PLATFORM_SIGNAL_REMOVED)
+		g_error ("Added/changed link not found in the local cache.");
+}
+
+void
+run_command (const char *format, ...)
+{
+	char *command;
+	va_list ap;
+
+	va_start (ap, format);
+	command = g_strdup_vprintf (format, ap);
+	va_end (ap);
+	debug ("Running command: %s", command);
+	g_assert (!system (command));
+	debug ("Command finished.");
+	g_free (command);
+}
+
+NMTST_DEFINE();
+
+int
+main (int argc, char **argv)
+{
+	int result;
+	const char *program = *argv;
+
+	nmtst_init_with_logging (&argc, &argv, NULL, "ALL");
+
+	if (SETUP == nm_linux_platform_setup && getuid() != 0) {
+		/* Try to exec as sudo, this function does not return, if a sudo-cmd is set. */
+		nmtst_reexec_sudo ();
+
+#ifdef REQUIRE_ROOT_TESTS
+		g_message ("Fail test: requires root privileges (%s)", program);
+		return EXIT_FAILURE;
+#else
+		g_message ("Skipping test: requires root privileges (%s)", program);
+		return 77;
+#endif
+	}
+
+	SETUP ();
+
+	setup_tests ();
+
+	result = g_test_run ();
+
+	nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME));
+
+	nm_platform_free ();
+	return result;
+}
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
new file mode 100644
index 00000000..8f6b3912
--- /dev/null
+++ b/src/platform/tests/test-common.h
@@ -0,0 +1,42 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "nm-logging.h"
+#include "nm-platform.h"
+#include "nm-fake-platform.h"
+#include "nm-linux-platform.h"
+
+#define DEVICE_NAME "nm-test-device"
+
+#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__)
+
+#define error(err) g_assert (nm_platform_get_error () == err)
+#define no_error() error (NM_PLATFORM_ERROR_NONE)
+
+typedef struct {
+	int handler_id;
+	const char *name;
+	NMPlatformSignalChangeType change_type;
+	gboolean received;
+	GMainLoop *loop;
+	int ifindex;
+	const char *ifname;
+} SignalData;
+
+SignalData *add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname);
+#define add_signal(name, change_type, callback) add_signal_full (name, change_type, (GCallback) callback, 0, NULL)
+#define add_signal_ifindex(name, change_type, callback, ifindex) add_signal_full (name, change_type, (GCallback) callback, ifindex, NULL)
+#define add_signal_ifname(name, change_type, callback, ifname) add_signal_full (name, change_type, (GCallback) callback, 0, ifname)
+void accept_signal (SignalData *data);
+void wait_signal (SignalData *data);
+void free_signal (SignalData *data);
+
+void link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data);
+
+void run_command (const char *format, ...);
+
+void setup_tests (void);
+
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
new file mode 100644
index 00000000..a3f757c2
--- /dev/null
+++ b/src/platform/tests/test-link.c
@@ -0,0 +1,557 @@
+#include "test-common.h"
+#include "nm-test-utils.h"
+
+#define LO_INDEX 1
+#define LO_NAME "lo"
+#define LO_TYPEDESC "loopback"
+
+#define DUMMY_TYPEDESC "dummy"
+#define BOGUS_NAME "nm-bogus-device"
+#define BOGUS_IFINDEX INT_MAX
+#define SLAVE_NAME "nm-test-slave"
+#define PARENT_NAME "nm-test-parent"
+#define VLAN_ID 4077
+#define VLAN_FLAGS 0
+#define MTU 1357
+
+static void
+test_bogus(void)
+{
+	size_t addrlen;
+
+	g_assert (!nm_platform_link_exists (BOGUS_NAME));
+	no_error ();
+	g_assert (!nm_platform_link_delete (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_get_ifindex (BOGUS_NAME));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_get_name (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_get_type (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_get_type_name (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+
+	g_assert (!nm_platform_link_set_up (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_set_down (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_set_arp (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_set_noarp (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_is_up (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_is_connected (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_uses_arp (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+
+	g_assert (!nm_platform_link_get_address (BOGUS_IFINDEX, &addrlen));
+	g_assert (!addrlen);
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_get_address (BOGUS_IFINDEX, NULL));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_set_mtu (BOGUS_IFINDEX, MTU));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_get_mtu (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+
+	g_assert (!nm_platform_link_supports_carrier_detect (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_supports_vlans (BOGUS_IFINDEX));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+
+	g_assert (!nm_platform_vlan_get_info (BOGUS_IFINDEX, NULL, NULL));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_vlan_set_ingress_map (BOGUS_IFINDEX, 0, 0));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_vlan_set_egress_map (BOGUS_IFINDEX, 0, 0));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+}
+
+static void
+test_loopback (void)
+{
+	g_assert (nm_platform_link_exists (LO_NAME));
+	g_assert_cmpint (nm_platform_link_get_type (LO_INDEX), ==, NM_LINK_TYPE_LOOPBACK);
+	g_assert_cmpint (nm_platform_link_get_ifindex (LO_NAME), ==, LO_INDEX);
+	g_assert_cmpstr (nm_platform_link_get_name (LO_INDEX), ==, LO_NAME);
+	g_assert_cmpstr (nm_platform_link_get_type_name (LO_INDEX), ==, LO_TYPEDESC);
+
+	g_assert (nm_platform_link_supports_carrier_detect (LO_INDEX));
+	g_assert (!nm_platform_link_supports_vlans (LO_INDEX));
+}
+
+static int
+software_add (NMLinkType link_type, const char *name)
+{
+	switch (link_type) {
+	case NM_LINK_TYPE_DUMMY:
+		return nm_platform_dummy_add (name);
+	case NM_LINK_TYPE_BRIDGE:
+		return nm_platform_bridge_add (name, NULL, 0);
+	case NM_LINK_TYPE_BOND:
+		{
+			gboolean bond0_exists = nm_platform_link_exists ("bond0");
+			gboolean result = nm_platform_bond_add (name);
+			NMPlatformError error = nm_platform_get_error ();
+
+			/* Check that bond0 is *not* automatically created. */
+			if (!bond0_exists)
+				g_assert (!nm_platform_link_exists ("bond0"));
+
+			nm_platform_set_error (error);
+			return result;
+		}
+	case NM_LINK_TYPE_TEAM:
+		return nm_platform_team_add (name);
+	case NM_LINK_TYPE_VLAN: {
+		SignalData *parent_added;
+		SignalData *parent_changed;
+
+		/* Don't call link_callback for the bridge interface */
+		parent_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME);
+		if (nm_platform_bridge_add (PARENT_NAME, NULL, 0))
+			accept_signal (parent_added);
+		free_signal (parent_added);
+
+		{
+			int parent_ifindex = nm_platform_link_get_ifindex (PARENT_NAME);
+
+			parent_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, parent_ifindex);
+			g_assert (nm_platform_link_set_up (parent_ifindex));
+			accept_signal (parent_changed);
+			free_signal (parent_changed);
+
+			return nm_platform_vlan_add (name, parent_ifindex, VLAN_ID, 0);
+		}
+	}
+	default:
+		g_error ("Link type %d unhandled.", link_type);
+	}
+}
+
+static void
+test_slave (int master, int type, SignalData *master_changed)
+{
+	int ifindex;
+	SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, SLAVE_NAME);
+	SignalData *link_changed, *link_removed;
+	char *value;
+
+	g_assert (software_add (type, SLAVE_NAME));
+	ifindex = nm_platform_link_get_ifindex (SLAVE_NAME);
+	g_assert (ifindex > 0);
+	link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex);
+	link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex);
+	accept_signal (link_added);
+
+	/* Set the slave up to see whether master's IFF_LOWER_UP is set correctly.
+	 *
+	 * See https://bugzilla.redhat.com/show_bug.cgi?id=910348
+	 */
+	g_assert (nm_platform_link_set_down (ifindex));
+	g_assert (!nm_platform_link_is_up (ifindex));
+	accept_signal (link_changed);
+
+	/* Enslave */
+	link_changed->ifindex = ifindex;
+	g_assert (nm_platform_link_enslave (master, ifindex)); no_error ();
+	g_assert_cmpint (nm_platform_link_get_master (ifindex), ==, master); no_error ();
+	accept_signal (link_changed);
+	accept_signal (master_changed);
+
+	/* Set master up */
+	g_assert (nm_platform_link_set_up (master));
+	accept_signal (master_changed);
+
+	/* Master with a disconnected slave is disconnected
+	 *
+	 * For some reason, bonding and teaming slaves are automatically set up. We
+	 * need to set them back down for this test.
+	 */
+	switch (nm_platform_link_get_type (master)) {
+	case NM_LINK_TYPE_BOND:
+	case NM_LINK_TYPE_TEAM:
+		g_assert (nm_platform_link_set_down (ifindex));
+		accept_signal (link_changed);
+		accept_signal (master_changed);
+		break;
+	default:
+		break;
+	}
+	g_assert (!nm_platform_link_is_up (ifindex));
+	g_assert (!nm_platform_link_is_connected (ifindex));
+	if (nm_platform_link_is_connected (master)) {
+		if (nm_platform_link_get_type (master) == NM_LINK_TYPE_TEAM) {
+			/* Older team versions (e.g. Fedora 17) have a bug that team master stays
+			 * IFF_LOWER_UP even if its slave is down. Double check it with iproute2 and if
+			 * `ip link` also claims master to be up, accept it. */
+			char *stdout = NULL;
+
+			nmtst_spawn_sync (NULL, &stdout, NULL, 0, "/sbin/ip", "link", "show", "dev", nm_platform_link_get_name (master));
+
+			g_assert (strstr (stdout, "LOWER_UP"));
+			g_free (stdout);
+		} else
+			g_assert_not_reached ();
+	}
+
+	/* Set slave up and see if master gets up too */
+	g_assert (nm_platform_link_set_up (ifindex)); no_error ();
+	g_assert (nm_platform_link_is_connected (ifindex));
+	g_assert (nm_platform_link_is_connected (master));
+	accept_signal (link_changed);
+	accept_signal (master_changed);
+
+	/* Enslave again
+	 *
+	 * Gracefully succeed if already enslaved.
+	 */
+	g_assert (nm_platform_link_enslave (master, ifindex)); no_error ();
+	accept_signal (link_changed);
+	accept_signal (master_changed);
+
+	/* Set slave option */
+	switch (type) {
+	case NM_LINK_TYPE_BRIDGE:
+		g_assert (nm_platform_slave_set_option (ifindex, "priority", "789"));
+		no_error ();
+		value = nm_platform_slave_get_option (ifindex, "priority");
+		no_error ();
+		g_assert_cmpstr (value, ==, "789");
+		g_free (value);
+		break;
+	default:
+		break;
+	}
+
+	/* Release */
+	g_assert (nm_platform_link_release (master, ifindex));
+	g_assert_cmpint (nm_platform_link_get_master (ifindex), ==, 0); no_error ();
+	accept_signal (link_changed);
+	accept_signal (master_changed);
+
+	/* Release again */
+	g_assert (!nm_platform_link_release (master, ifindex));
+	error (NM_PLATFORM_ERROR_NOT_SLAVE);
+
+	/* Remove */
+	g_assert (nm_platform_link_delete (ifindex));
+	no_error ();
+	accept_signal (link_removed);
+
+	free_signal (link_added);
+	free_signal (link_changed);
+	free_signal (link_removed);
+}
+
+static void
+test_software (NMLinkType link_type, const char *link_typename)
+{
+	int ifindex;
+	char *value;
+	int vlan_parent, vlan_id;
+
+	SignalData *link_added, *link_changed, *link_removed;
+
+	/* Add */
+	link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME);
+	g_assert (software_add (link_type, DEVICE_NAME));
+	no_error ();
+	accept_signal (link_added);
+	g_assert (nm_platform_link_exists (DEVICE_NAME));
+	ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	g_assert (ifindex >= 0);
+	g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, link_type);
+	g_assert_cmpstr (nm_platform_link_get_type_name (ifindex), ==, link_typename);
+	link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex);
+	link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex);
+	if (link_type == NM_LINK_TYPE_VLAN) {
+		g_assert (nm_platform_vlan_get_info (ifindex, &vlan_parent, &vlan_id));
+		g_assert_cmpint (vlan_parent, ==, nm_platform_link_get_ifindex (PARENT_NAME));
+		g_assert_cmpint (vlan_id, ==, VLAN_ID);
+		no_error ();
+	}
+
+	/* Add again */
+	g_assert (!software_add (link_type, DEVICE_NAME));
+	error (NM_PLATFORM_ERROR_EXISTS);
+
+	/* Set ARP/NOARP */
+	g_assert (nm_platform_link_uses_arp (ifindex));
+	g_assert (nm_platform_link_set_noarp (ifindex));
+	g_assert (!nm_platform_link_uses_arp (ifindex));
+	accept_signal (link_changed);
+	g_assert (nm_platform_link_set_arp (ifindex));
+	g_assert (nm_platform_link_uses_arp (ifindex));
+	accept_signal (link_changed);
+
+	/* Set master option */
+	switch (link_type) {
+	case NM_LINK_TYPE_BRIDGE:
+		g_assert (nm_platform_master_set_option (ifindex, "forward_delay", "789"));
+		no_error ();
+		value = nm_platform_master_get_option (ifindex, "forward_delay");
+		no_error ();
+		g_assert_cmpstr (value, ==, "789");
+		g_free (value);
+		break;
+	case NM_LINK_TYPE_BOND:
+		g_assert (nm_platform_master_set_option (ifindex, "mode", "active-backup"));
+		no_error ();
+		value = nm_platform_master_get_option (ifindex, "mode");
+		no_error ();
+		/* When reading back, the output looks slightly different. */
+		g_assert (g_str_has_prefix (value, "active-backup"));
+		g_free (value);
+		break;
+	default:
+		break;
+	}
+
+	/* Enslave and release */
+	switch (link_type) {
+	case NM_LINK_TYPE_BRIDGE:
+	case NM_LINK_TYPE_BOND:
+	case NM_LINK_TYPE_TEAM:
+		link_changed->ifindex = ifindex;
+		test_slave (ifindex, NM_LINK_TYPE_DUMMY, link_changed);
+		link_changed->ifindex = 0;
+		break;
+	default:
+		break;
+	}
+
+	/* Delete */
+	g_assert (nm_platform_link_delete (ifindex));
+	no_error ();
+	g_assert (!nm_platform_link_exists (DEVICE_NAME)); no_error ();
+	g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, NM_LINK_TYPE_NONE);
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	g_assert (!nm_platform_link_get_type (ifindex));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+	accept_signal (link_removed);
+
+	/* Delete again */
+	g_assert (!nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+
+	/* VLAN: Delete parent */
+	if (link_type == NM_LINK_TYPE_VLAN) {
+		SignalData *link_removed_parent = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, vlan_parent);
+
+		g_assert (nm_platform_link_delete (vlan_parent));
+		accept_signal (link_removed_parent);
+		free_signal (link_removed_parent);
+	}
+
+	/* No pending signal */
+	free_signal (link_added);
+	free_signal (link_changed);
+	free_signal (link_removed);
+}
+
+static void
+test_bridge (void)
+{
+	test_software (NM_LINK_TYPE_BRIDGE, "bridge");
+}
+
+static void
+test_bond (void)
+{
+	if (SETUP == nm_linux_platform_setup &&
+	    !g_file_test ("/proc/1/net/bonding", G_FILE_TEST_IS_DIR) &&
+	    system("modprobe --show bonding") != 0) {
+		g_test_skip ("Skipping test for bonding: bonding module not available");
+		return;
+	}
+
+	test_software (NM_LINK_TYPE_BOND, "bond");
+}
+
+static void
+test_team (void)
+{
+	test_software (NM_LINK_TYPE_TEAM, "team");
+}
+
+static void
+test_vlan ()
+{
+	test_software (NM_LINK_TYPE_VLAN, "vlan");
+}
+
+static void
+test_internal (void)
+{
+	SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME);
+	SignalData *link_changed, *link_removed;
+	const char mac[6] = { 0x00, 0xff, 0x11, 0xee, 0x22, 0xdd };
+	const char *address;
+	size_t addrlen;
+	int ifindex;
+
+	/* Check the functions for non-existent devices */
+	g_assert (!nm_platform_link_exists (DEVICE_NAME)); no_error ();
+	g_assert (!nm_platform_link_get_ifindex (DEVICE_NAME));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+
+	/* Add device */
+	g_assert (nm_platform_dummy_add (DEVICE_NAME));
+	no_error ();
+	accept_signal (link_added);
+
+	/* Try to add again */
+	g_assert (!nm_platform_dummy_add (DEVICE_NAME));
+	error (NM_PLATFORM_ERROR_EXISTS);
+
+	/* Check device index, name and type */
+	ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	g_assert (ifindex > 0);
+	g_assert_cmpstr (nm_platform_link_get_name (ifindex), ==, DEVICE_NAME);
+	g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, NM_LINK_TYPE_DUMMY);
+	g_assert_cmpstr (nm_platform_link_get_type_name (ifindex), ==, DUMMY_TYPEDESC);
+	link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex);
+	link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex);
+
+	/* Up/connected */
+	g_assert (!nm_platform_link_is_up (ifindex)); no_error ();
+	g_assert (!nm_platform_link_is_connected (ifindex)); no_error ();
+	g_assert (nm_platform_link_set_up (ifindex)); no_error ();
+	g_assert (nm_platform_link_is_up (ifindex)); no_error ();
+	g_assert (nm_platform_link_is_connected (ifindex)); no_error ();
+	accept_signal (link_changed);
+	g_assert (nm_platform_link_set_down (ifindex)); no_error ();
+	g_assert (!nm_platform_link_is_up (ifindex)); no_error ();
+	g_assert (!nm_platform_link_is_connected (ifindex)); no_error ();
+	accept_signal (link_changed);
+
+	/* arp/noarp */
+	g_assert (!nm_platform_link_uses_arp (ifindex));
+	g_assert (nm_platform_link_set_arp (ifindex));
+	g_assert (nm_platform_link_uses_arp (ifindex));
+	accept_signal (link_changed);
+	g_assert (nm_platform_link_set_noarp (ifindex));
+	g_assert (!nm_platform_link_uses_arp (ifindex));
+	accept_signal (link_changed);
+
+	/* Features */
+	g_assert (!nm_platform_link_supports_carrier_detect (ifindex));
+	g_assert (nm_platform_link_supports_vlans (ifindex));
+
+	/* Set MAC address */
+	g_assert (nm_platform_link_set_address (ifindex, mac, sizeof (mac)));
+	address = nm_platform_link_get_address (ifindex, &addrlen);
+	g_assert (addrlen == sizeof(mac));
+	g_assert (!memcmp (address, mac, addrlen));
+	address = nm_platform_link_get_address (ifindex, NULL);
+	g_assert (!memcmp (address, mac, addrlen));
+	accept_signal (link_changed);
+
+	/* Set MTU */
+	g_assert (nm_platform_link_set_mtu (ifindex, MTU));
+	no_error ();
+	g_assert_cmpint (nm_platform_link_get_mtu (ifindex), ==, MTU);
+	accept_signal (link_changed);
+
+	/* Delete device */
+	g_assert (nm_platform_link_delete (ifindex));
+	no_error ();
+	accept_signal (link_removed);
+
+	/* Try to delete again */
+	g_assert (!nm_platform_link_delete (ifindex));
+	error (NM_PLATFORM_ERROR_NOT_FOUND);
+
+	free_signal (link_added);
+	free_signal (link_changed);
+	free_signal (link_removed);
+}
+
+static void
+test_external (void)
+{
+	NMPlatformLink link;
+	SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME);
+	SignalData *link_changed, *link_removed;
+	int ifindex;
+	gboolean success;
+
+	run_command ("ip link add %s type %s", DEVICE_NAME, "dummy");
+	wait_signal (link_added);
+	g_assert (nm_platform_link_exists (DEVICE_NAME));
+	ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	g_assert (ifindex > 0);
+	g_assert_cmpstr (nm_platform_link_get_name (ifindex), ==, DEVICE_NAME);
+	g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, NM_LINK_TYPE_DUMMY);
+	g_assert_cmpstr (nm_platform_link_get_type_name (ifindex), ==, DUMMY_TYPEDESC);
+	link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex);
+	link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex);
+
+	success = nm_platform_link_get (ifindex, &link);
+	g_assert (success);
+	if (!link.driver) {
+		/* we still lack the notification via UDEV. Expect another link changed signal. */
+		wait_signal (link_changed);
+	}
+
+	/* Up/connected/arp */
+	g_assert (!nm_platform_link_is_up (ifindex));
+	g_assert (!nm_platform_link_is_connected (ifindex));
+	g_assert (!nm_platform_link_uses_arp (ifindex));
+	run_command ("ip link set %s up", DEVICE_NAME);
+	wait_signal (link_changed);
+	g_assert (nm_platform_link_is_up (ifindex));
+	g_assert (nm_platform_link_is_connected (ifindex));
+	run_command ("ip link set %s down", DEVICE_NAME);
+	wait_signal (link_changed);
+	g_assert (!nm_platform_link_is_up (ifindex));
+	g_assert (!nm_platform_link_is_connected (ifindex));
+	/* This test doesn't trigger a netlink event at least on
+	 * 3.8.2-206.fc18.x86_64. Disabling the waiting and checking code
+	 * because of that.
+	 */
+	run_command ("ip link set %s arp on", DEVICE_NAME);
+#if 0
+	wait_signal (link_changed);
+	g_assert (nm_platform_link_uses_arp (ifindex));
+#endif
+	run_command ("ip link set %s arp off", DEVICE_NAME);
+#if 0
+	wait_signal (link_changed);
+	g_assert (!nm_platform_link_uses_arp (ifindex));
+#endif
+
+	run_command ("ip link del %s", DEVICE_NAME);
+	wait_signal (link_removed);
+	g_assert (!nm_platform_link_exists (DEVICE_NAME));
+
+	free_signal (link_added);
+	free_signal (link_changed);
+	free_signal (link_removed);
+}
+
+void
+setup_tests (void)
+{
+	nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME));
+	nm_platform_link_delete (nm_platform_link_get_ifindex (SLAVE_NAME));
+	nm_platform_link_delete (nm_platform_link_get_ifindex (PARENT_NAME));
+	g_assert (!nm_platform_link_exists (DEVICE_NAME));
+	g_assert (!nm_platform_link_exists (SLAVE_NAME));
+	g_assert (!nm_platform_link_exists (PARENT_NAME));
+
+	g_test_add_func ("/link/bogus", test_bogus);
+	g_test_add_func ("/link/loopback", test_loopback);
+	g_test_add_func ("/link/internal", test_internal);
+	g_test_add_func ("/link/software/bridge", test_bridge);
+	g_test_add_func ("/link/software/bond", test_bond);
+	g_test_add_func ("/link/software/team", test_team);
+	g_test_add_func ("/link/software/vlan", test_vlan);
+
+	if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform"))
+		g_test_add_func ("/link/external", test_external);
+}
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
new file mode 100644
index 00000000..d45730ac
--- /dev/null
+++ b/src/platform/tests/test-route.c
@@ -0,0 +1,255 @@
+#include "test-common.h"
+#include "nm-test-utils.h"
+
+#define DEVICE_NAME "nm-test-device"
+
+static void
+ip4_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
+{
+	g_assert (received);
+	g_assert_cmpint (received->ifindex, ==, ifindex);
+	g_assert (data && data->name);
+	g_assert_cmpstr (data->name, ==, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED);
+
+	if (data->ifindex && data->ifindex != received->ifindex)
+		return;
+	if (data->change_type != change_type)
+		return;
+
+	if (data->loop)
+		g_main_loop_quit (data->loop);
+
+	if (data->received)
+		g_error ("Received signal '%s' a second time.", data->name);
+
+	data->received = TRUE;
+}
+
+static void
+ip6_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data)
+{
+	g_assert (received);
+	g_assert_cmpint (received->ifindex, ==, ifindex);
+	g_assert (data && data->name);
+	g_assert_cmpstr (data->name, ==, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED);
+
+	if (data->ifindex && data->ifindex != received->ifindex)
+		return;
+	if (data->change_type != change_type)
+		return;
+
+	if (data->loop)
+		g_main_loop_quit (data->loop);
+
+	if (data->received)
+		g_error ("Received signal '%s' a second time.", data->name);
+
+	data->received = TRUE;
+}
+
+static void
+test_ip4_route ()
+{
+	int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback);
+	SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback);
+	SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback);
+	GArray *routes;
+	NMPlatformIP4Route rts[3];
+	in_addr_t network;
+	int plen = 24;
+	in_addr_t gateway;
+	int metric = 20;
+	int mss = 1000;
+
+	inet_pton (AF_INET, "192.0.3.0", &network);
+	inet_pton (AF_INET, "198.51.100.1", &gateway);
+
+	/* Add route to gateway */
+	g_assert (nm_platform_ip4_route_add (ifindex, NM_PLATFORM_SOURCE_USER, gateway, 32, INADDR_ANY, metric, mss));
+	no_error ();
+	accept_signal (route_added);
+
+	/* Add route */
+	g_assert (!nm_platform_ip4_route_exists (ifindex, network, plen, metric));
+	no_error ();
+	g_assert (nm_platform_ip4_route_add (ifindex, NM_PLATFORM_SOURCE_USER, network, plen, gateway, metric, mss));
+	no_error ();
+	g_assert (nm_platform_ip4_route_exists (ifindex, network, plen, metric));
+	no_error ();
+	accept_signal (route_added);
+
+	/* Add route again */
+	g_assert (nm_platform_ip4_route_add (ifindex, NM_PLATFORM_SOURCE_USER, network, plen, gateway, metric, mss));
+	no_error ();
+	accept_signal (route_changed);
+
+	/* Add default route */
+	g_assert (!nm_platform_ip4_route_exists (ifindex, 0, 0, metric));
+	no_error ();
+	g_assert (nm_platform_ip4_route_add (ifindex, NM_PLATFORM_SOURCE_USER, 0, 0, gateway, metric, mss));
+	no_error ();
+	g_assert (nm_platform_ip4_route_exists (ifindex, 0, 0, metric));
+	no_error ();
+	accept_signal (route_added);
+
+	/* Add default route again */
+	g_assert (nm_platform_ip4_route_add (ifindex, NM_PLATFORM_SOURCE_USER, 0, 0, gateway, metric, mss));
+	no_error ();
+	accept_signal (route_changed);
+
+	/* Test route listing */
+	routes = nm_platform_ip4_route_get_all (ifindex, TRUE);
+	memset (rts, 0, sizeof (rts));
+	rts[0].source = NM_PLATFORM_SOURCE_USER;
+	rts[0].network = gateway;
+	rts[0].plen = 32;
+	rts[0].ifindex = ifindex;
+	rts[0].gateway = INADDR_ANY;
+	rts[0].metric = metric;
+	rts[0].mss = mss;
+	rts[1].source = NM_PLATFORM_SOURCE_USER;
+	rts[1].network = network;
+	rts[1].plen = plen;
+	rts[1].ifindex = ifindex;
+	rts[1].gateway = gateway;
+	rts[1].metric = metric;
+	rts[1].mss = mss;
+	rts[2].source = NM_PLATFORM_SOURCE_USER;
+	rts[2].network = 0;
+	rts[2].plen = 0;
+	rts[2].ifindex = ifindex;
+	rts[2].gateway = gateway;
+	rts[2].metric = metric;
+	rts[2].mss = mss;
+	g_assert_cmpint (routes->len, ==, 3);
+	g_assert (!memcmp (routes->data, rts, sizeof (rts)));
+	nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len);
+	g_array_unref (routes);
+
+	/* Remove route */
+	g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric));
+	no_error ();
+	g_assert (!nm_platform_ip4_route_exists (ifindex, network, plen, metric));
+	accept_signal (route_removed);
+
+	/* Remove route again */
+	g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric));
+	no_error ();
+
+	free_signal (route_added);
+	free_signal (route_changed);
+	free_signal (route_removed);
+}
+
+static void
+test_ip6_route ()
+{
+	int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
+	SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_route_callback);
+	SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_route_callback);
+	SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_route_callback);
+	GArray *routes;
+	NMPlatformIP6Route rts[3];
+	struct in6_addr network;
+	int plen = 64;
+	struct in6_addr gateway;
+	int metric = 20;
+	int mss = 1000;
+
+	inet_pton (AF_INET6, "2001:db8:a:b:0:0:0:0", &network);
+	inet_pton (AF_INET6, "2001:db8:c:d:1:2:3:4", &gateway);
+
+	/* Add route to gateway */
+	g_assert (nm_platform_ip6_route_add (ifindex, NM_PLATFORM_SOURCE_USER, gateway, 128, in6addr_any, metric, mss));
+	no_error ();
+	accept_signal (route_added);
+
+	/* Add route */
+	g_assert (!nm_platform_ip6_route_exists (ifindex, network, plen, metric));
+	no_error ();
+	g_assert (nm_platform_ip6_route_add (ifindex, NM_PLATFORM_SOURCE_USER, network, plen, gateway, metric, mss));
+	no_error ();
+	g_assert (nm_platform_ip6_route_exists (ifindex, network, plen, metric));
+	no_error ();
+	accept_signal (route_added);
+
+	/* Add route again */
+	g_assert (nm_platform_ip6_route_add (ifindex, NM_PLATFORM_SOURCE_USER, network, plen, gateway, metric, mss));
+	no_error ();
+	accept_signal (route_changed);
+
+	/* Add default route */
+	g_assert (!nm_platform_ip6_route_exists (ifindex, in6addr_any, 0, metric));
+	no_error ();
+	g_assert (nm_platform_ip6_route_add (ifindex, NM_PLATFORM_SOURCE_USER, in6addr_any, 0, gateway, metric, mss));
+	no_error ();
+	g_assert (nm_platform_ip6_route_exists (ifindex, in6addr_any, 0, metric));
+	no_error ();
+	accept_signal (route_added);
+
+	/* Add default route again */
+	g_assert (nm_platform_ip6_route_add (ifindex, NM_PLATFORM_SOURCE_USER, in6addr_any, 0, gateway, metric, mss));
+	no_error ();
+	accept_signal (route_changed);
+
+	/* Test route listing */
+	routes = nm_platform_ip6_route_get_all (ifindex, TRUE);
+	memset (rts, 0, sizeof (rts));
+	rts[0].source = NM_PLATFORM_SOURCE_USER;
+	rts[0].network = gateway;
+	rts[0].plen = 128;
+	rts[0].ifindex = ifindex;
+	rts[0].gateway = in6addr_any;
+	rts[0].metric = metric;
+	rts[0].mss = mss;
+	rts[1].source = NM_PLATFORM_SOURCE_USER;
+	rts[1].network = network;
+	rts[1].plen = plen;
+	rts[1].ifindex = ifindex;
+	rts[1].gateway = gateway;
+	rts[1].metric = metric;
+	rts[1].mss = mss;
+	rts[2].source = NM_PLATFORM_SOURCE_USER;
+	rts[2].network = in6addr_any;
+	rts[2].plen = 0;
+	rts[2].ifindex = ifindex;
+	rts[2].gateway = gateway;
+	rts[2].metric = metric;
+	rts[2].mss = mss;
+	g_assert_cmpint (routes->len, ==, 3);
+	g_assert (!memcmp (routes->data, rts, sizeof (rts)));
+	nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len);
+	g_array_unref (routes);
+
+	/* Remove route */
+	g_assert (nm_platform_ip6_route_delete (ifindex, network, plen, metric));
+	no_error ();
+	g_assert (!nm_platform_ip6_route_exists (ifindex, network, plen, metric));
+	accept_signal (route_removed);
+
+	/* Remove route again */
+	g_assert (nm_platform_ip6_route_delete (ifindex, network, plen, metric));
+	no_error ();
+
+	free_signal (route_added);
+	free_signal (route_changed);
+	free_signal (route_removed);
+}
+
+void
+setup_tests (void)
+{
+	SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME);
+
+	nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME));
+	g_assert (!nm_platform_link_exists (DEVICE_NAME));
+	g_assert (nm_platform_dummy_add (DEVICE_NAME));
+	accept_signal (link_added);
+	free_signal (link_added);
+
+	g_assert (nm_platform_link_set_up (nm_platform_link_get_ifindex (DEVICE_NAME)));
+
+	g_test_add_func ("/route/ip4", test_ip4_route);
+	g_test_add_func ("/route/ip6", test_ip6_route);
+}
diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c
new file mode 100644
index 00000000..25ebd1b3
--- /dev/null
+++ b/src/platform/wifi/wifi-utils-nl80211.c
@@ -0,0 +1,971 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2005 - 2011 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ * Copyright (C) 2011 Intel Corporation. All rights reserved.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/ethernet.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+
+#include <linux/nl80211.h>
+
+#include "wifi-utils-private.h"
+#include "wifi-utils-nl80211.h"
+#include "nm-platform.h"
+#include "nm-logging.h"
+#include "nm-utils.h"
+
+typedef struct {
+	WifiData parent;
+	struct nl_sock *nl_sock;
+	int id;
+	struct nl_cb *nl_cb;
+	guint32 *freqs;
+	int num_freqs;
+	int phy;
+} WifiDataNl80211;
+
+static int
+ack_handler (struct nl_msg *msg, void *arg)
+{
+	int *done = arg;
+	*done = 1;
+	return NL_STOP;
+}
+
+static int
+finish_handler (struct nl_msg *msg, void *arg)
+{
+	int *done = arg;
+	*done = 1;
+	return NL_SKIP;
+}
+
+static int
+error_handler (struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
+{
+	int *done = arg;
+	*done = err->error;
+	return NL_SKIP;
+}
+
+static struct nl_msg *
+_nl80211_alloc_msg (int id, int ifindex, int phy, guint32 cmd, guint32 flags)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc ();
+	if (msg) {
+		genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0);
+		NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex);
+		if (phy != -1)
+			NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY, phy);
+	}
+	return msg;
+
+ nla_put_failure:
+	nlmsg_free (msg);
+	return NULL;
+}
+
+static struct nl_msg *
+nl80211_alloc_msg (WifiDataNl80211 *nl80211, guint32 cmd, guint32 flags)
+{
+	return _nl80211_alloc_msg (nl80211->id, nl80211->parent.ifindex, nl80211->phy, cmd, flags);
+}
+
+/* NOTE: this function consumes 'msg' */
+static int
+_nl80211_send_and_recv (struct nl_sock *nl_sock,
+                        struct nl_cb *nl_cb,
+                        struct nl_msg *msg,
+                        int (*valid_handler) (struct nl_msg *, void *),
+                        void *valid_data)
+{
+	struct nl_cb *cb;
+	int err, done;
+
+	g_return_val_if_fail (msg != NULL, -ENOMEM);
+
+	cb = nl_cb_clone (nl_cb);
+	if (!cb) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	err = nl_send_auto_complete (nl_sock, msg);
+	if (err < 0)
+		goto out;
+
+	done = 0;
+	nl_cb_err (cb, NL_CB_CUSTOM, error_handler, &done);
+	nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &done);
+	nl_cb_set (cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &done);
+	if (valid_handler)
+		nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data);
+
+	/* Loop until one of our NL callbacks says we're done; on success
+	 * done will be 1, on error it will be < 0.
+	 */
+	while (!done) {
+		err = nl_recvmsgs (nl_sock, cb);
+		if (err && err != -NLE_AGAIN) {
+			/* Kernel scan list can change while we are dumping it, as new scan
+			 * results from H/W can arrive. BSS info is assured to be consistent
+			 * and we don't need consistent view of whole scan list. Hence do
+			 * not warn on DUMP_INTR error for get scan command.
+			 */
+			if (err == -NLE_DUMP_INTR &&
+			    genlmsg_hdr(nlmsg_hdr(msg))->cmd == NL80211_CMD_GET_SCAN)
+				break;
+
+			nm_log_warn (LOGD_WIFI, "nl_recvmsgs() error: (%d) %s",
+			             err, nl_geterror (err));
+			break;
+		}
+	}
+	if (err == 0 && done < 0)
+		err = done;
+
+ out:
+	nl_cb_put (cb);
+	nlmsg_free (msg);
+	return err;
+}
+
+static int
+nl80211_send_and_recv (WifiDataNl80211 *nl80211,
+                       struct nl_msg *msg,
+                       int (*valid_handler) (struct nl_msg *, void *),
+                       void *valid_data)
+{
+	return _nl80211_send_and_recv (nl80211->nl_sock, nl80211->nl_cb, msg,
+	                               valid_handler, valid_data);
+}
+
+static void
+wifi_nl80211_deinit (WifiData *parent)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) parent;
+
+	if (nl80211->nl_sock)
+		nl_socket_free (nl80211->nl_sock);
+	if (nl80211->nl_cb)
+		nl_cb_put (nl80211->nl_cb);
+	g_free (nl80211->freqs);
+}
+
+struct nl80211_iface_info {
+	NM80211Mode mode;
+};
+
+static int
+nl80211_iface_info_handler (struct nl_msg *msg, void *arg)
+{
+	struct nl80211_iface_info *info = arg;
+	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+	if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
+		       genlmsg_attrlen (gnlh, 0), NULL) < 0)
+		return NL_SKIP;
+
+	if (!tb[NL80211_ATTR_IFTYPE])
+		return NL_SKIP;
+
+	switch (nla_get_u32 (tb[NL80211_ATTR_IFTYPE])) {
+	case NL80211_IFTYPE_ADHOC:
+		info->mode = NM_802_11_MODE_ADHOC;
+		break;
+	case NL80211_IFTYPE_AP:
+		info->mode = NM_802_11_MODE_AP;
+		break;
+	case NL80211_IFTYPE_STATION:
+		info->mode = NM_802_11_MODE_INFRA;
+		break;
+	}
+
+	return NL_SKIP;
+}
+
+static NM80211Mode
+wifi_nl80211_get_mode (WifiData *data)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	struct nl80211_iface_info iface_info = {
+		.mode = NM_802_11_MODE_UNKNOWN,
+	};
+	struct nl_msg *msg;
+
+	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_INTERFACE, 0);
+
+	if (nl80211_send_and_recv (nl80211, msg, nl80211_iface_info_handler,
+				   &iface_info) < 0)
+		return NM_802_11_MODE_UNKNOWN;
+
+	return iface_info.mode;
+}
+
+static gboolean
+wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	struct nl_msg *msg;
+	int err;
+
+	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_INTERFACE, 0);
+
+	switch (mode) {
+	case NM_802_11_MODE_INFRA:
+		NLA_PUT_U32 (msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION);
+		break;
+	case NM_802_11_MODE_ADHOC:
+		NLA_PUT_U32 (msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_ADHOC);
+		break;
+	case NM_802_11_MODE_AP:
+		NLA_PUT_U32 (msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+
+	err = nl80211_send_and_recv (nl80211, msg, NULL, NULL);
+	return err ? FALSE : TRUE;
+
+ nla_put_failure:
+	nlmsg_free (msg);
+	return FALSE;
+}
+
+/* @divisor: pass what value @xbm should be divided by to get dBm */
+static guint32
+nl80211_xbm_to_percent (gint32 xbm, guint32 divisor)
+{
+#define NOISE_FLOOR_DBM  -90
+#define SIGNAL_MAX_DBM   -20
+
+	xbm /= divisor;
+	xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM);
+
+	return 100 - 70 * (((float) SIGNAL_MAX_DBM - (float) xbm) /
+			   ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM));
+}
+
+struct nl80211_bss_info {
+	guint32 freq;
+	guint8 bssid[ETH_ALEN];
+	guint8 ssid[32];
+	guint32 ssid_len;
+	guint32 beacon_signal;
+	gboolean valid;
+};
+
+#define WLAN_EID_SSID	0
+
+static void
+find_ssid (guint8 *ies, guint32 ies_len,
+           guint8 **ssid, guint32 *ssid_len)
+{
+	*ssid = NULL;
+	*ssid_len = 0;
+
+	while (ies_len > 2 && ies[0] != WLAN_EID_SSID) {
+		ies_len -= ies[1] + 2;
+		ies += ies[1] + 2;
+	}
+	if (ies_len < 2)
+		return;
+	if (ies_len < 2 + ies[1])
+		return;
+
+	*ssid_len = ies[1];
+	*ssid = ies + 2;
+}
+
+static int
+nl80211_bss_dump_handler (struct nl_msg *msg, void *arg)
+{
+	struct nl80211_bss_info *info = arg;
+	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct nlattr *bss[NL80211_BSS_MAX + 1];
+	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+		[NL80211_BSS_TSF] = { .type = NLA_U64 },
+		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_BSS_BSSID] = { },
+		[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+		[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+		[NL80211_BSS_INFORMATION_ELEMENTS] = { },
+		[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+		[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
+	};
+	guint32 status;
+
+	if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
+		       genlmsg_attrlen (gnlh, 0), NULL) < 0)
+		return NL_SKIP;
+
+	if (tb[NL80211_ATTR_BSS] == NULL)
+		return NL_SKIP;
+
+	if (nla_parse_nested (bss, NL80211_BSS_MAX,
+			      tb[NL80211_ATTR_BSS],
+			      bss_policy))
+		return NL_SKIP;
+
+	if (bss[NL80211_BSS_STATUS] == NULL)
+		return NL_SKIP;
+
+	status = nla_get_u32 (bss[NL80211_BSS_STATUS]);
+
+	if (status != NL80211_BSS_STATUS_ASSOCIATED &&
+	    status != NL80211_BSS_STATUS_IBSS_JOINED)
+		return NL_SKIP;
+
+	if (bss[NL80211_BSS_BSSID] == NULL)
+		return NL_SKIP;
+	memcpy(info->bssid, nla_data (bss[NL80211_BSS_BSSID]), ETH_ALEN);
+
+	if (bss[NL80211_BSS_FREQUENCY])
+		info->freq = nla_get_u32 (bss[NL80211_BSS_FREQUENCY]);
+
+	if (bss[NL80211_BSS_SIGNAL_UNSPEC])
+		info->beacon_signal =
+			nla_get_u8 (bss[NL80211_BSS_SIGNAL_UNSPEC]);
+
+	if (bss[NL80211_BSS_SIGNAL_MBM])
+		info->beacon_signal =
+			nl80211_xbm_to_percent (nla_get_u32 (bss[NL80211_BSS_SIGNAL_MBM]), 100);
+
+	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+		guint8 *ssid;
+		guint32 ssid_len;
+
+		find_ssid(nla_data (bss[NL80211_BSS_INFORMATION_ELEMENTS]),
+			  nla_len (bss[NL80211_BSS_INFORMATION_ELEMENTS]),
+			  &ssid, &ssid_len);
+		if (ssid && ssid_len && ssid_len <= sizeof (info->ssid)) {
+			memcpy (info->ssid, ssid, ssid_len);
+			info->ssid_len = ssid_len;
+		}
+	}
+
+	info->valid = TRUE;
+
+	return NL_SKIP;
+}
+
+static void
+nl80211_get_bss_info (WifiDataNl80211 *nl80211,
+                      struct nl80211_bss_info *bss_info)
+{
+	struct nl_msg *msg;
+
+	memset(bss_info, 0, sizeof (*bss_info));
+
+	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_SCAN, NLM_F_DUMP);
+
+	nl80211_send_and_recv (nl80211, msg, nl80211_bss_dump_handler, bss_info);
+}
+
+static guint32
+wifi_nl80211_get_freq (WifiData *data)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	struct nl80211_bss_info bss_info;
+
+	nl80211_get_bss_info (nl80211, &bss_info);
+
+	return bss_info.freq;
+}
+
+static guint32
+wifi_nl80211_find_freq (WifiData *data, const guint32 *freqs)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	int i;
+
+	for (i = 0; i < nl80211->num_freqs; i++) {
+		while (*freqs) {
+			if (nl80211->freqs[i] == *freqs)
+				return *freqs;
+			freqs++;
+		}
+	}
+	return 0;
+}
+
+static GByteArray *
+wifi_nl80211_get_ssid (WifiData *data)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	GByteArray *array = NULL;
+	struct nl80211_bss_info bss_info;
+
+	nl80211_get_bss_info (nl80211, &bss_info);
+
+	if (bss_info.valid) {
+		array = g_byte_array_sized_new (bss_info.ssid_len);
+		g_byte_array_append (array, (const guint8 *) bss_info.ssid,
+				     bss_info.ssid_len);
+	}
+
+	return array;
+}
+
+static gboolean
+wifi_nl80211_get_bssid (WifiData *data, struct ether_addr *out_bssid)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	struct nl80211_bss_info bss_info;
+
+	nl80211_get_bss_info (nl80211, &bss_info);
+
+	if (bss_info.valid)
+		memcpy(out_bssid, bss_info.bssid, ETH_ALEN);
+
+	return bss_info.valid;
+}
+
+struct nl80211_station_info {
+	guint32 txrate;
+	gboolean txrate_valid;
+	guint8 signal;
+	gboolean signal_valid;
+};
+
+static int
+nl80211_station_handler (struct nl_msg *msg, void *arg)
+{
+	struct nl80211_station_info *info = arg;
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
+	struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+	struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+	static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+		[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
+		[NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
+		[NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
+		[NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
+	};
+
+	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+		[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+		[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+	};
+
+	if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0),
+	               genlmsg_attrlen (gnlh, 0), NULL) < 0)
+		return NL_SKIP;
+
+	if (tb[NL80211_ATTR_STA_INFO] == NULL)
+		return NL_SKIP;
+
+	if (nla_parse_nested (sinfo, NL80211_STA_INFO_MAX,
+	                      tb[NL80211_ATTR_STA_INFO],
+	                      stats_policy))
+		return NL_SKIP;
+
+	if (sinfo[NL80211_STA_INFO_TX_BITRATE] == NULL)
+		return NL_SKIP;
+
+	if (nla_parse_nested (rinfo, NL80211_RATE_INFO_MAX,
+	                      sinfo[NL80211_STA_INFO_TX_BITRATE],
+	                      rate_policy))
+		return NL_SKIP;
+
+	if (rinfo[NL80211_RATE_INFO_BITRATE] == NULL)
+		return NL_SKIP;
+
+	/* convert from nl80211's units of 100kbps to NM's kbps */
+	info->txrate = nla_get_u16 (rinfo[NL80211_RATE_INFO_BITRATE]) * 100;
+	info->txrate_valid = TRUE;
+
+	if (sinfo[NL80211_STA_INFO_SIGNAL] != NULL) {
+		info->signal = nl80211_xbm_to_percent ((gint8) nla_get_u8 (sinfo[NL80211_STA_INFO_SIGNAL]), 1);
+		info->signal_valid = TRUE;
+	}
+
+	return NL_SKIP;
+}
+
+static void
+nl80211_get_ap_info (WifiDataNl80211 *nl80211,
+                     struct nl80211_station_info *sta_info)
+{
+	struct nl_msg *msg;
+	struct nl80211_bss_info bss_info;
+
+	memset(sta_info, 0, sizeof (*sta_info));
+
+	nl80211_get_bss_info (nl80211, &bss_info);
+	if (!bss_info.valid)
+		return;
+
+	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_STATION, 0);
+	if (msg) {
+		NLA_PUT (msg, NL80211_ATTR_MAC, ETH_ALEN, bss_info.bssid);
+
+		nl80211_send_and_recv (nl80211, msg, nl80211_station_handler, sta_info);
+		if (!sta_info->signal_valid) {
+			/* Fall back to bss_info signal quality (both are in percent) */
+			sta_info->signal = bss_info.beacon_signal;
+		}
+	}
+
+	return;
+
+ nla_put_failure:
+	nlmsg_free (msg);
+	return;
+}
+
+static guint32
+wifi_nl80211_get_rate (WifiData *data)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	struct nl80211_station_info sta_info;
+
+	nl80211_get_ap_info (nl80211, &sta_info);
+
+	return sta_info.txrate;
+}
+
+static int
+wifi_nl80211_get_qual (WifiData *data)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	struct nl80211_station_info sta_info;
+
+	nl80211_get_ap_info (nl80211, &sta_info);
+	return sta_info.signal;
+}
+
+#if HAVE_NL80211_CRITICAL_PROTOCOL_CMDS
+static gboolean
+wifi_nl80211_indicate_addressing_running (WifiData *data, gboolean running)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	struct nl_msg *msg;
+	int err;
+
+	msg = nl80211_alloc_msg (nl80211,
+	                         running ? NL80211_CMD_CRIT_PROTOCOL_START :
+	                         NL80211_CMD_CRIT_PROTOCOL_STOP,
+	                         0);
+	/* Despite the DHCP name, we're using this for any type of IP addressing,
+	 * DHCPv4, DHCPv6, and IPv6 SLAAC.
+	 */
+	NLA_PUT_U16 (msg, NL80211_ATTR_CRIT_PROT_ID, NL80211_CRIT_PROTO_DHCP);
+	if (running) {
+		/* Give DHCP 5 seconds to complete */
+		NLA_PUT_U16 (msg, NL80211_ATTR_MAX_CRIT_PROT_DURATION, 5000);
+	}
+
+	err = nl80211_send_and_recv (nl80211, msg, NULL, NULL);
+	return err ? FALSE : TRUE;
+
+nla_put_failure:
+	nlmsg_free (msg);
+	return FALSE;
+}
+#endif
+
+struct nl80211_wowlan_info {
+	gboolean enabled;
+};
+
+static int
+nl80211_wowlan_handler (struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
+	struct nl80211_wowlan_info *info = arg;
+
+	info->enabled = FALSE;
+
+	if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+	               genlmsg_attrlen (gnlh, 0), NULL) < 0)
+		return NL_SKIP;
+
+	if (tb[NL80211_ATTR_WOWLAN_TRIGGERS])
+		info->enabled = TRUE;
+
+	return NL_SKIP;
+}
+
+static gboolean
+wifi_nl80211_get_wowlan (WifiData *data)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	struct nl_msg *msg;
+	struct nl80211_wowlan_info info;
+
+	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WOWLAN, 0);
+	nl80211_send_and_recv (nl80211, msg, nl80211_wowlan_handler, &info);
+
+	return info.enabled;
+}
+
+struct nl80211_device_info {
+	int phy;
+	guint32 *freqs;
+	int num_freqs;
+	guint32 caps;
+	gboolean can_scan;
+	gboolean can_scan_ssid;
+	gboolean supported;
+	gboolean success;
+	gboolean can_wowlan;
+};
+
+#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00
+#define WLAN_CIPHER_SUITE_WEP40     0x000FAC01
+#define WLAN_CIPHER_SUITE_TKIP      0x000FAC02
+#define WLAN_CIPHER_SUITE_CCMP      0x000FAC04
+#define WLAN_CIPHER_SUITE_WEP104    0x000FAC05
+#define WLAN_CIPHER_SUITE_AES_CMAC  0x000FAC06
+#define WLAN_CIPHER_SUITE_GCMP      0x000FAC08
+#define WLAN_CIPHER_SUITE_SMS4      0x00147201
+
+static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
+	struct nl80211_device_info *info = arg;
+	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+	struct nlattr *nl_band;
+	struct nlattr *nl_freq;
+	int rem_freq;
+	int rem_band;
+	int freq_idx;
+	static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
+		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+	};
+
+	if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+	               genlmsg_attrlen (gnlh, 0), NULL) < 0)
+		return NL_SKIP;
+
+	if (   tb[NL80211_ATTR_WIPHY] == NULL
+	    || tb[NL80211_ATTR_WIPHY_BANDS] == NULL)
+		return NL_SKIP;
+
+	info->phy = nla_get_u32 (tb[NL80211_ATTR_WIPHY]);
+
+	if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) {
+		info->can_scan_ssid =
+			nla_get_u8 (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) > 0;
+	} else {
+		/* old kernel that only had mac80211, so assume it can */
+		info->can_scan_ssid = TRUE;
+	}
+
+	if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
+		struct nlattr *nl_cmd;
+		int i;
+
+		nla_for_each_nested (nl_cmd, tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
+			switch (nla_get_u32 (nl_cmd)) {
+			case NL80211_CMD_TRIGGER_SCAN:
+				info->can_scan = TRUE;
+				break;
+			case NL80211_CMD_CONNECT:
+			case NL80211_CMD_AUTHENTICATE:
+				/* Only devices that support CONNECT or AUTH actually support
+				 * 802.11, unlike say ipw2x00 (up to at least kernel 3.4) which
+				 * has minimal info support, but no actual command support.
+				 * This check mirrors what wpa_supplicant does to determine
+				 * whether or not to use the nl80211 driver.
+				 */
+				info->supported = TRUE;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	info->num_freqs = 0;
+
+	nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
+		if (nla_parse_nested (tb_band, NL80211_BAND_ATTR_MAX, nl_band,
+				      NULL) < 0)
+			return NL_SKIP;
+
+		nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS],
+				    rem_freq) {
+			nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+					  nl_freq, freq_policy);
+
+			if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+				continue;
+
+			info->num_freqs++;
+		}
+	}
+
+	info->freqs = g_malloc0 (sizeof (guint32) * info->num_freqs);
+
+	freq_idx = 0;
+	nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) {
+		if (nla_parse_nested (tb_band, NL80211_BAND_ATTR_MAX, nl_band,
+		                      NULL) < 0)
+			return NL_SKIP;
+
+		nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS],
+		                    rem_freq) {
+			nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+			                  nl_freq, freq_policy);
+
+			if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+				continue;
+
+			info->freqs[freq_idx] =
+				nla_get_u32 (tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+			freq_idx++;
+		}
+	}
+
+	if (tb[NL80211_ATTR_CIPHER_SUITES]) {
+		int num;
+		int i;
+		__u32 *ciphers = nla_data (tb[NL80211_ATTR_CIPHER_SUITES]);
+
+		num = nla_len (tb[NL80211_ATTR_CIPHER_SUITES]) / sizeof (__u32);
+		for (i = 0; i < num; i++) {
+			switch (ciphers[i]) {
+			case WLAN_CIPHER_SUITE_WEP40:
+				info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40;
+				break;
+			case WLAN_CIPHER_SUITE_WEP104:
+				info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP104;
+				break;
+			case WLAN_CIPHER_SUITE_TKIP:
+				info->caps |= (NM_WIFI_DEVICE_CAP_CIPHER_TKIP |
+				               NM_WIFI_DEVICE_CAP_WPA);
+				break;
+			case WLAN_CIPHER_SUITE_CCMP:
+				info->caps |= (NM_WIFI_DEVICE_CAP_CIPHER_CCMP |
+				               NM_WIFI_DEVICE_CAP_RSN);
+				break;
+			case WLAN_CIPHER_SUITE_AES_CMAC:
+			case WLAN_CIPHER_SUITE_GCMP:
+			case WLAN_CIPHER_SUITE_SMS4:
+				break;
+			default:
+				nm_log_dbg (LOGD_HW | LOGD_WIFI, "Don't know the meaning of NL80211_ATTR_CIPHER_SUITE %#8.8x.", ciphers[i]);
+				break;
+			}
+		}
+	}
+
+	if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) {
+		struct nlattr *nl_mode;
+		int i;
+
+		nla_for_each_nested (nl_mode, tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
+			if (nla_type (nl_mode) == NL80211_IFTYPE_AP)
+				info->caps |= NM_WIFI_DEVICE_CAP_AP;
+			else if (nla_type (nl_mode) == NL80211_IFTYPE_ADHOC)
+				info->caps |= NM_WIFI_DEVICE_CAP_ADHOC;
+		}
+	}
+
+	if (tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED])
+		info->can_wowlan = TRUE;
+
+	info->success = TRUE;
+
+	return NL_SKIP;
+}
+
+WifiData *
+wifi_nl80211_init (const char *iface, int ifindex)
+{
+	WifiDataNl80211 *nl80211;
+	struct nl_msg *msg;
+	struct nl80211_device_info device_info = {};
+
+	nl80211 = wifi_data_new (iface, ifindex, sizeof (*nl80211));
+	nl80211->parent.get_mode = wifi_nl80211_get_mode;
+	nl80211->parent.set_mode = wifi_nl80211_set_mode;
+	nl80211->parent.get_freq = wifi_nl80211_get_freq;
+	nl80211->parent.find_freq = wifi_nl80211_find_freq;
+	nl80211->parent.get_ssid = wifi_nl80211_get_ssid;
+	nl80211->parent.get_bssid = wifi_nl80211_get_bssid;
+	nl80211->parent.get_rate = wifi_nl80211_get_rate;
+	nl80211->parent.get_qual = wifi_nl80211_get_qual;
+#if HAVE_NL80211_CRITICAL_PROTOCOL_CMDS
+	nl80211->parent.indicate_addressing_running = wifi_nl80211_indicate_addressing_running;
+#endif
+	nl80211->parent.deinit = wifi_nl80211_deinit;
+
+	nl80211->nl_sock = nl_socket_alloc ();
+	if (nl80211->nl_sock == NULL)
+		goto error;
+
+	if (genl_connect (nl80211->nl_sock))
+		goto error;
+
+	nl80211->id = genl_ctrl_resolve (nl80211->nl_sock, "nl80211");
+	if (nl80211->id < 0)
+		goto error;
+
+	nl80211->nl_cb = nl_cb_alloc (NL_CB_DEFAULT);
+	if (nl80211->nl_cb == NULL)
+		goto error;
+
+	nl80211->phy = -1;
+
+	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WIPHY, 0);
+
+	if (nl80211_send_and_recv (nl80211, msg, nl80211_wiphy_info_handler,
+	                           &device_info) < 0) {
+		nm_log_dbg (LOGD_HW | LOGD_WIFI,
+				    "(%s): NL80211_CMD_GET_WIPHY request failed",
+				    nl80211->parent.iface);
+		goto error;
+	}
+
+	if (!device_info.success) {
+		nm_log_dbg (LOGD_HW | LOGD_WIFI,
+				    "(%s): NL80211_CMD_GET_WIPHY request indicated failure",
+				    nl80211->parent.iface);
+		goto error;
+	}
+
+	if (!device_info.supported) {
+		nm_log_dbg (LOGD_HW | LOGD_WIFI,
+				    "(%s): driver does not fully support nl80211, falling back to WEXT",
+				    nl80211->parent.iface);
+		goto error;
+	}
+
+	if (!device_info.can_scan_ssid) {
+		nm_log_err (LOGD_HW | LOGD_WIFI,
+		            "(%s): driver does not support SSID scans",
+		            nl80211->parent.iface);
+		goto error;
+	}
+
+	if (device_info.num_freqs == 0 || device_info.freqs == NULL) {
+		nm_log_err (LOGD_HW | LOGD_WIFI,
+				    "(%s): driver reports no supported frequencies",
+				    nl80211->parent.iface);
+		goto error;
+	}
+
+	if (device_info.caps == 0) {
+		nm_log_err (LOGD_HW | LOGD_WIFI,
+		            "(%s): driver doesn't report support of any encryption",
+		            nl80211->parent.iface);
+		goto error;
+	}
+
+	nl80211->phy = device_info.phy;
+	nl80211->freqs = device_info.freqs;
+	nl80211->num_freqs = device_info.num_freqs;
+	nl80211->parent.caps = device_info.caps;
+
+	if (device_info.can_wowlan)
+		nl80211->parent.get_wowlan = wifi_nl80211_get_wowlan;
+
+	nm_log_info (LOGD_HW | LOGD_WIFI,
+	             "(%s): using nl80211 for WiFi device control",
+	             nl80211->parent.iface);
+
+	return (WifiData *) nl80211;
+
+error:
+	wifi_utils_deinit ((WifiData *) nl80211);
+	return NULL;
+}
+
+gboolean
+wifi_nl80211_is_wifi (const char *iface)
+{
+	struct nl_sock *nl_sock;
+	struct nl_cb *nl_cb = NULL;
+	struct nl_msg *msg = NULL;
+	int id, ifindex;
+	struct nl80211_iface_info iface_info = {
+		.mode = NM_802_11_MODE_UNKNOWN,
+	};
+	gboolean is_wifi = FALSE;
+
+	nl_sock = nl_socket_alloc ();
+	if (nl_sock == NULL)
+		return FALSE;
+
+	if (genl_connect (nl_sock))
+		goto error;
+
+	ifindex = nm_platform_link_get_ifindex (iface);
+	if (ifindex < 0)
+		goto error;
+
+	id = genl_ctrl_resolve (nl_sock, "nl80211");
+	if (id < 0)
+		goto error;
+
+	nl_cb = nl_cb_alloc (NL_CB_DEFAULT);
+	if (nl_cb) {
+		msg = _nl80211_alloc_msg (id, ifindex, -1, NL80211_CMD_GET_INTERFACE, 0);
+		if (_nl80211_send_and_recv (nl_sock,
+			                        nl_cb,
+			                        msg,
+			                        nl80211_iface_info_handler,
+			                        &iface_info) >= 0)
+			is_wifi = (iface_info.mode != NM_802_11_MODE_UNKNOWN);
+	}
+
+ error:
+	if (nl_cb)
+		nl_cb_put (nl_cb);
+	nl_socket_free (nl_sock);
+	return is_wifi;
+}
+
diff --git a/src/platform/wifi/wifi-utils-nl80211.h b/src/platform/wifi/wifi-utils-nl80211.h
new file mode 100644
index 00000000..2a7fe874
--- /dev/null
+++ b/src/platform/wifi/wifi-utils-nl80211.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2011 Intel Corporation. All rights reserved.
+ */
+
+#ifndef WIFI_UTILS_NL80211_H
+#define WIFI_UTILS_NL80211_H
+
+#include "wifi-utils.h"
+
+WifiData *wifi_nl80211_init (const char *iface, int ifindex);
+
+gboolean wifi_nl80211_is_wifi (const char *iface);
+
+#endif  /* WIFI_UTILS_NL80211_H */
diff --git a/src/platform/wifi/wifi-utils-private.h b/src/platform/wifi/wifi-utils-private.h
new file mode 100644
index 00000000..e7601752
--- /dev/null
+++ b/src/platform/wifi/wifi-utils-private.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ */
+
+#ifndef WIFI_UTILS_PRIVATE_H
+#define WIFI_UTILS_PRIVATE_H
+
+#include <glib.h>
+
+#include "NetworkManager.h"
+#include "wifi-utils.h"
+
+struct WifiData {
+	char *iface;
+	int ifindex;
+	NMDeviceWifiCapabilities caps;
+
+	NM80211Mode (*get_mode) (WifiData *data);
+
+	gboolean (*set_mode) (WifiData *data, const NM80211Mode mode);
+
+	/* Return current frequency in MHz (really associated BSS frequency) */
+	guint32 (*get_freq) (WifiData *data);
+
+	/* Return first supported frequency in the zero-terminated list */
+	guint32 (*find_freq) (WifiData *data, const guint32 *freqs);
+
+	/* If SSID is empty/blank (zero-length or all \0s) return NULL */
+	GByteArray * (*get_ssid) (WifiData *data);
+
+	/* Return current bitrate in Kbps */
+	guint32 (*get_rate) (WifiData *data);
+
+	gboolean (*get_bssid) (WifiData *data, struct ether_addr *out_bssid);
+
+	/* Return a signal strength percentage 0 - 100% for the current BSSID;
+	 * return -1 on errors or if not associated.
+	 */
+	int (*get_qual) (WifiData *data);
+
+	void (*deinit) (WifiData *data);
+
+	gboolean (*get_wowlan) (WifiData *data);
+
+	/* OLPC Mesh-only functions */
+
+	guint32 (*get_mesh_channel) (WifiData *data);
+
+	/* channel == 0 means "auto channel" */
+	gboolean (*set_mesh_channel) (WifiData *data, guint32 channel);
+
+	/* ssid == NULL means "auto SSID" */
+	gboolean (*set_mesh_ssid) (WifiData *data, const GByteArray *ssid);
+
+	gboolean (*indicate_addressing_running) (WifiData *data, gboolean running);
+};
+
+gpointer wifi_data_new (const char *iface, int ifindex, gsize len);
+void wifi_data_free (WifiData *data);
+
+#endif  /* WIFI_UTILS_PRIVATE_H */
diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c
new file mode 100644
index 00000000..cc94b69a
--- /dev/null
+++ b/src/platform/wifi/wifi-utils-wext.c
@@ -0,0 +1,671 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2005 - 2011 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/ethernet.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include "wifi-utils-private.h"
+#include "wifi-utils-wext.h"
+#include "nm-logging.h"
+#include "nm-utils.h"
+
+/* Hacks necessary to #include wireless.h; yay for WEXT */
+#ifndef __user
+#define __user
+#endif
+#include <sys/types.h>
+#include <linux/types.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <linux/wireless.h>
+
+
+typedef struct {
+	WifiData parent;
+	int fd;
+	struct iw_quality max_qual;
+	gint8 num_freqs;
+	guint32 freqs[IW_MAX_FREQUENCIES];
+} WifiDataWext;
+
+/* Until a new wireless-tools comes out that has the defs and the structure,
+ * need to copy them here.
+ */
+/* Scan capability flags - in (struct iw_range *)->scan_capa */
+#define NM_IW_SCAN_CAPA_NONE    0x00
+#define NM_IW_SCAN_CAPA_ESSID   0x01
+
+struct iw_range_with_scan_capa
+{
+	guint32 throughput;
+	guint32 min_nwid;
+	guint32 max_nwid;
+	guint16 old_num_channels;
+	guint8  old_num_frequency;
+
+	guint8  scan_capa;
+	/* don't need the rest... */
+};
+
+static guint32
+iw_freq_to_uint32 (struct iw_freq *freq)
+{
+	if (freq->e == 0) {
+		/* Some drivers report channel not frequency.  Convert to a
+		 * frequency; but this assumes that the device is in b/g mode.
+		 */
+		if ((freq->m >= 1) && (freq->m <= 13))
+			return 2407 + (5 * freq->m);
+		else if (freq->m == 14)
+			return 2484;
+	}
+
+	return (guint32) (((double) freq->m) * pow (10, freq->e) / 1000000);
+}
+
+static void
+wifi_wext_deinit (WifiData *parent)
+{
+	WifiDataWext *wext = (WifiDataWext *) parent;
+
+	if (wext->fd >= 0)
+		close (wext->fd);
+}
+
+static NM80211Mode
+wifi_wext_get_mode (WifiData *data)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	struct iwreq wrq;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+
+	if (ioctl (wext->fd, SIOCGIWMODE, &wrq) < 0) {
+		if (errno != ENODEV) {
+			nm_log_warn (LOGD_HW | LOGD_WIFI,
+			             "(%s): error %d getting card mode",
+			             wext->parent.iface, errno);
+		}
+		return NM_802_11_MODE_UNKNOWN;
+	}
+
+	switch (wrq.u.mode) {
+	case IW_MODE_ADHOC:
+		return NM_802_11_MODE_ADHOC;
+	case IW_MODE_MASTER:
+		return NM_802_11_MODE_AP;
+	case IW_MODE_INFRA:
+		return NM_802_11_MODE_INFRA;
+	default:
+		break;
+	}
+	return NM_802_11_MODE_UNKNOWN;
+}
+
+static gboolean
+wifi_wext_set_mode (WifiData *data, const NM80211Mode mode)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	struct iwreq wrq;
+
+	if (wifi_wext_get_mode (data) == mode)
+		return TRUE;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	switch (mode) {
+	case NM_802_11_MODE_ADHOC:
+		wrq.u.mode = IW_MODE_ADHOC;
+		break;
+	case NM_802_11_MODE_AP:
+		wrq.u.mode = IW_MODE_MASTER;
+		break;
+	case NM_802_11_MODE_INFRA:
+		wrq.u.mode = IW_MODE_INFRA;
+		break;
+	default:
+		g_warn_if_reached ();
+		return FALSE;
+	}
+
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	if (ioctl (wext->fd, SIOCSIWMODE, &wrq) < 0) {
+		if (errno != ENODEV) {
+			nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): error setting mode %d",
+			            wext->parent.iface, mode);
+		}
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static guint32
+wifi_wext_get_freq (WifiData *data)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	struct iwreq wrq;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	if (ioctl (wext->fd, SIOCGIWFREQ, &wrq) < 0) {
+		nm_log_warn (LOGD_HW | LOGD_WIFI,
+		             "(%s): error getting frequency: %s",
+		             wext->parent.iface, strerror (errno));
+		return 0;
+	}
+
+	return iw_freq_to_uint32 (&wrq.u.freq);
+}
+
+static guint32
+wifi_wext_find_freq (WifiData *data, const guint32 *freqs)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	int i;
+
+	for (i = 0; i < wext->num_freqs; i++) {
+		while (*freqs) {
+			if (wext->freqs[i] == *freqs)
+				return *freqs;
+			freqs++;
+		}
+	}
+	return 0;
+}
+
+static GByteArray *
+wifi_wext_get_ssid (WifiData *data)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	struct iwreq wrq;
+	char ssid[IW_ESSID_MAX_SIZE + 2];
+	guint32 len;
+	GByteArray *array = NULL;
+
+	memset (ssid, 0, sizeof (ssid));
+	wrq.u.essid.pointer = (caddr_t) &ssid;
+	wrq.u.essid.length = sizeof (ssid);
+	wrq.u.essid.flags = 0;
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+
+	if (ioctl (wext->fd, SIOCGIWESSID, &wrq) < 0) {
+		nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): couldn't get SSID: %d",
+		            wext->parent.iface, errno);
+		return NULL;
+	}
+
+	len = wrq.u.essid.length;
+	if (nm_utils_is_empty_ssid ((guint8 *) ssid, len) == FALSE) {
+		array = g_byte_array_sized_new (len);
+		g_byte_array_append (array, (const guint8 *) ssid, len);
+	}
+
+	return array;
+}
+
+static gboolean
+wifi_wext_get_bssid (WifiData *data, struct ether_addr *out_bssid)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	struct iwreq wrq;
+
+	memset (&wrq, 0, sizeof (wrq));
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	if (ioctl (wext->fd, SIOCGIWAP, &wrq) < 0) {
+		nm_log_warn (LOGD_HW | LOGD_WIFI,
+		             "(%s): error getting associated BSSID: %s",
+		             wext->parent.iface, strerror (errno));
+		return FALSE;
+	}
+	memcpy (out_bssid->ether_addr_octet, &(wrq.u.ap_addr.sa_data), ETH_ALEN);
+	return TRUE;
+}
+
+static guint32
+wifi_wext_get_rate (WifiData *data)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	struct iwreq wrq;
+	int err;
+
+	memset (&wrq, 0, sizeof (wrq));
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	err = ioctl (wext->fd, SIOCGIWRATE, &wrq);
+	return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0);
+}
+
+static int
+wext_qual_to_percent (const struct iw_quality *qual,
+                      const struct iw_quality *max_qual)
+{
+	int percent = -1;
+	int level_percent = -1;
+
+	g_return_val_if_fail (qual != NULL, -1);
+	g_return_val_if_fail (max_qual != NULL, -1);
+
+	/* Magically convert the many different WEXT quality representations to a percentage */
+
+	nm_log_dbg (LOGD_WIFI,
+	            "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X  ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X",
+	            (__s8) qual->qual, qual->qual, qual->qual,
+	            (__s8) qual->level, qual->level, qual->level,
+	            (__s8) qual->noise, qual->noise, qual->noise,
+	            qual->updated,
+	            (__s8) max_qual->qual, max_qual->qual, max_qual->qual,
+	            (__s8) max_qual->level, max_qual->level, max_qual->level,
+	            (__s8) max_qual->noise, max_qual->noise, max_qual->noise,
+	            max_qual->updated);
+
+	/* Try using the card's idea of the signal quality first as long as it tells us what the max quality is.
+	 * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be
+	 * bounded by 0 and max_qual->qual, and MUST change in a linear fashion.  Within those bounds, drivers
+	 * are free to use whatever they want to calculate "Link Quality".
+	 */
+	if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID))
+		percent = (int)(100 * ((double)qual->qual / (double)max_qual->qual));
+
+	/* If the driver doesn't specify a complete and valid quality, we have two options:
+	 *
+	 * 1) dBm: driver must specify max_qual->level = 0, and have valid values for
+	 *        qual->level and (qual->noise OR max_qual->noise)
+	 * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for
+	 *        qual->level and max_qual->level
+	 *
+	 * This is the WEXT spec.  If this interpretation is wrong, I'll fix it.  Otherwise,
+	 * If drivers don't conform to it, they are wrong and need to be fixed.
+	 */
+
+	if (    (max_qual->level == 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID)      /* Valid max_qual->level == 0 */
+	    && !(qual->updated & IW_QUAL_LEVEL_INVALID)                                     /* Must have valid qual->level */
+	    && (    ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */
+	        || ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID)))         /*    OR valid qual->noise */
+	   ) {
+		/* Absolute power values (dBm) */
+
+		/* Reasonable fallbacks for dumb drivers that don't specify either level. */
+		#define FALLBACK_NOISE_FLOOR_DBM  -90
+		#define FALLBACK_SIGNAL_MAX_DBM   -20
+		int max_level = FALLBACK_SIGNAL_MAX_DBM;
+		int noise = FALLBACK_NOISE_FLOOR_DBM;
+		int level = qual->level - 0x100;
+
+		level = CLAMP (level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM);
+
+		if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))
+			noise = qual->noise - 0x100;
+		else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID))
+			noise = max_qual->noise - 0x100;
+		noise = CLAMP (noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM);
+
+		/* A sort of signal-to-noise ratio calculation */
+		level_percent = (int) (100 - 70 * (((double)max_level - (double)level) /
+		                                   ((double)max_level - (double)noise)));
+		nm_log_dbg (LOGD_WIFI, "QL1: level_percent is %d.  max_level %d, level %d, noise_floor %d.",
+		            level_percent, max_level, level, noise);
+	} else if (   (max_qual->level != 0)
+	           && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */
+	           && !(qual->updated & IW_QUAL_LEVEL_INVALID)) {
+		/* Relative power values (RSSI) */
+
+		int level = qual->level;
+
+		/* Signal level is relavtive (0 -> max_qual->level) */
+		level = CLAMP (level, 0, max_qual->level);
+		level_percent = (int)(100 * ((double)level / (double)max_qual->level));
+		nm_log_dbg (LOGD_WIFI, "QL2: level_percent is %d.  max_level %d, level %d.",
+		            level_percent, max_qual->level, level);
+	} else if (percent == -1) {
+		nm_log_dbg (LOGD_WIFI, "QL: Could not get quality %% value from driver.  Driver is probably buggy.");
+	}
+
+	/* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */
+	if ((percent < 1) && (level_percent >= 0))
+		percent = level_percent;
+
+	nm_log_dbg (LOGD_WIFI, "QL: Final quality percent is %d (%d).",
+	            percent, CLAMP (percent, 0, 100));
+	return (CLAMP (percent, 0, 100));
+}
+
+static int
+wifi_wext_get_qual (WifiData *data)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	struct iwreq wrq;
+	struct iw_statistics stats;
+
+	memset (&stats, 0, sizeof (stats));
+	wrq.u.data.pointer = &stats;
+	wrq.u.data.length = sizeof (stats);
+	wrq.u.data.flags = 1;  /* Clear updated flag */
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+
+	if (ioctl (wext->fd, SIOCGIWSTATS, &wrq) < 0) {
+		nm_log_warn (LOGD_HW | LOGD_WIFI,
+		             "(%s): error getting signal strength: %s",
+		             wext->parent.iface, strerror (errno));
+		return -1;
+	}
+
+	return wext_qual_to_percent (&stats.qual, &wext->max_qual);
+}
+
+/*********************/
+/* OLPC Mesh-only functions */
+
+static guint32
+wifi_wext_get_mesh_channel (WifiData *data)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	guint32 freq;
+	int i;
+
+	freq = wifi_utils_get_freq (data);
+	for (i = 0; i < wext->num_freqs; i++) {
+		if (freq == wext->freqs[i])
+			return i + 1;
+	}
+	return 0;
+}
+
+static gboolean
+wifi_wext_set_mesh_channel (WifiData *data, guint32 channel)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	struct iwreq wrq;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+
+	if (channel > 0) {
+		wrq.u.freq.flags = IW_FREQ_FIXED;
+		wrq.u.freq.e = 0;
+		wrq.u.freq.m = channel;
+	}
+
+	if (ioctl (wext->fd, SIOCSIWFREQ, &wrq) < 0) {
+		nm_log_err (LOGD_HW | LOGD_WIFI | LOGD_OLPC,
+		            "(%s): error setting channel to %d: %s",
+		            wext->parent.iface, channel, strerror (errno));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+wifi_wext_set_mesh_ssid (WifiData *data, const GByteArray *ssid)
+{
+	WifiDataWext *wext = (WifiDataWext *) data;
+	struct iwreq wrq;
+	guint32 len = 0;
+	char buf[IW_ESSID_MAX_SIZE + 1];
+
+	memset (buf, 0, sizeof (buf));
+	if (ssid) {
+		len = ssid->len;
+		memcpy (buf, ssid->data, MIN (sizeof (buf) - 1, len));
+	}
+	wrq.u.essid.pointer = (caddr_t) buf;
+	wrq.u.essid.length = len;
+	wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */
+
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	if (ioctl (wext->fd, SIOCSIWESSID, &wrq) == 0)
+		return TRUE;
+
+	if (errno != ENODEV) {
+		nm_log_err (LOGD_HW | LOGD_WIFI | LOGD_OLPC,
+		            "(%s): error setting SSID to '%s': %s",
+		            wext->parent.iface,
+		            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(null)",
+		            strerror (errno));
+	}
+
+	return FALSE;
+}
+
+/*********************/
+
+static gboolean
+wext_can_scan (WifiDataWext *wext)
+{
+	struct iwreq wrq;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	if (ioctl (wext->fd, SIOCSIWSCAN, &wrq) < 0) {
+		if (errno == EOPNOTSUPP)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean
+wext_get_range (WifiDataWext *wext,
+                struct iw_range *range,
+                guint32 *response_len)
+{
+	int i = 26;
+	gboolean success = FALSE;
+	struct iwreq wrq;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	wrq.u.data.pointer = (caddr_t) range;
+	wrq.u.data.length = sizeof (struct iw_range);
+
+	/* Need to give some drivers time to recover after suspend/resume
+	 * (ex ipw3945 takes a few seconds to talk to its regulatory daemon;
+	 * see rh bz#362421)
+	 */
+	while (i-- > 0) {
+		if (ioctl (wext->fd, SIOCGIWRANGE, &wrq) == 0) {
+			if (response_len)
+				*response_len = wrq.u.data.length;
+			success = TRUE;
+			break;
+		} else if (errno != EAGAIN) {
+			nm_log_err (LOGD_HW | LOGD_WIFI,
+			            "(%s): couldn't get driver range information (%d).",
+			            wext->parent.iface, errno);
+			break;
+		}
+
+		g_usleep (G_USEC_PER_SEC / 4);
+	}
+
+	if (i <= 0) {
+		nm_log_warn (LOGD_HW | LOGD_WIFI,
+		             "(%s): driver took too long to respond to IWRANGE query.",
+		             wext->parent.iface);
+	}
+
+	return success;
+}
+
+#define WPA_CAPS (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | \
+                  NM_WIFI_DEVICE_CAP_CIPHER_CCMP | \
+                  NM_WIFI_DEVICE_CAP_WPA | \
+                  NM_WIFI_DEVICE_CAP_RSN)
+
+static guint32
+wext_get_caps (WifiDataWext *wext, struct iw_range *range)
+{
+	guint32 caps = NM_WIFI_DEVICE_CAP_NONE;
+
+	g_return_val_if_fail (wext != NULL, NM_WIFI_DEVICE_CAP_NONE);
+	g_return_val_if_fail (range != NULL, NM_WIFI_DEVICE_CAP_NONE);
+
+	/* All drivers should support WEP by default */
+	caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104;
+
+	if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
+		caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP;
+
+	if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
+		caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP;
+
+	if (range->enc_capa & IW_ENC_CAPA_WPA)
+		caps |= NM_WIFI_DEVICE_CAP_WPA;
+
+	if (range->enc_capa & IW_ENC_CAPA_WPA2)
+		caps |= NM_WIFI_DEVICE_CAP_RSN;
+
+	/* Check for cipher support but not WPA support */
+	if (    (caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
+	    && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) {
+		nm_log_warn (LOGD_WIFI, "%s: device supports WPA ciphers but not WPA protocol; "
+		             "WPA unavailable.", wext->parent.iface);
+		caps &= ~WPA_CAPS;
+	}
+
+	/* Check for WPA support but not cipher support */
+	if (    (caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))
+	    && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) {
+		nm_log_warn (LOGD_WIFI, "%s: device supports WPA protocol but not WPA ciphers; "
+		             "WPA unavailable.", wext->parent.iface);
+		caps &= ~WPA_CAPS;
+	}
+
+	/* There's no way to detect Ad-Hoc/AP mode support with WEXT
+	 * (other than actually trying to do it), so just assume that
+	 * Ad-Hoc is supported and AP isn't.
+	 */
+	caps |= NM_WIFI_DEVICE_CAP_ADHOC;
+
+	return caps;
+}
+
+WifiData *
+wifi_wext_init (const char *iface, int ifindex, gboolean check_scan)
+{
+	WifiDataWext *wext;
+	struct iw_range range;
+	guint32 response_len = 0;
+	struct iw_range_with_scan_capa *scan_capa_range;
+	int i;
+
+	wext = wifi_data_new (iface, ifindex, sizeof (*wext));
+	wext->parent.get_mode = wifi_wext_get_mode;
+	wext->parent.set_mode = wifi_wext_set_mode;
+	wext->parent.get_freq = wifi_wext_get_freq;
+	wext->parent.find_freq = wifi_wext_find_freq;
+	wext->parent.get_ssid = wifi_wext_get_ssid;
+	wext->parent.get_bssid = wifi_wext_get_bssid;
+	wext->parent.get_rate = wifi_wext_get_rate;
+	wext->parent.get_qual = wifi_wext_get_qual;
+	wext->parent.deinit = wifi_wext_deinit;
+	wext->parent.get_mesh_channel = wifi_wext_get_mesh_channel;
+	wext->parent.set_mesh_channel = wifi_wext_set_mesh_channel;
+	wext->parent.set_mesh_ssid = wifi_wext_set_mesh_ssid;
+
+	wext->fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (wext->fd < 0)
+		goto error;
+
+	memset (&range, 0, sizeof (struct iw_range));
+	if (wext_get_range (wext, &range, &response_len) == FALSE) {
+		nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver WEXT range request failed",
+		             wext->parent.iface);
+		goto error;
+	}
+
+	if ((response_len < 300) || (range.we_version_compiled < 21)) {
+		nm_log_info (LOGD_HW | LOGD_WIFI,
+		             "(%s): driver WEXT version too old (got %d, expected >= 21)",
+		             wext->parent.iface,
+		             range.we_version_compiled);
+		goto error;
+	}
+
+	wext->max_qual.qual = range.max_qual.qual;
+	wext->max_qual.level = range.max_qual.level;
+	wext->max_qual.noise = range.max_qual.noise;
+	wext->max_qual.updated = range.max_qual.updated;
+
+	wext->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES);
+	for (i = 0; i < wext->num_freqs; i++)
+		wext->freqs[i] = iw_freq_to_uint32 (&range.freq[i]);
+
+	/* Check for scanning capability; cards that can't scan are not supported */
+	if (check_scan && (wext_can_scan (wext) == FALSE)) {
+		nm_log_info (LOGD_HW | LOGD_WIFI,
+		             "(%s): drivers that cannot scan are unsupported",
+		             wext->parent.iface);
+		goto error;
+	}
+
+	/* Check for the ability to scan specific SSIDs.  Until the scan_capa
+	 * field gets added to wireless-tools, need to work around that by casting
+	 * to the custom structure.
+	 */
+	scan_capa_range = (struct iw_range_with_scan_capa *) &range;
+	if (scan_capa_range->scan_capa & NM_IW_SCAN_CAPA_ESSID) {
+		nm_log_info (LOGD_HW | LOGD_WIFI,
+		             "(%s): driver supports SSID scans (scan_capa 0x%02X).",
+		             wext->parent.iface,
+		             scan_capa_range->scan_capa);
+	} else {
+		nm_log_info (LOGD_HW | LOGD_WIFI,
+		             "(%s): driver does not support SSID scans (scan_capa 0x%02X).",
+		             wext->parent.iface,
+		             scan_capa_range->scan_capa);
+	}
+
+	wext->parent.caps = wext_get_caps (wext, &range);
+
+	nm_log_info (LOGD_HW | LOGD_WIFI,
+	             "(%s): using WEXT for WiFi device control",
+	             wext->parent.iface);
+
+	return (WifiData *) wext;
+
+error:
+	wifi_utils_deinit ((WifiData *) wext);
+	return NULL;
+}
+
+gboolean
+wifi_wext_is_wifi (const char *iface)
+{
+	int fd;
+	struct iwreq iwr;
+	gboolean is_wifi = FALSE;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd >= 0) {
+		strncpy (iwr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
+		if (ioctl (fd, SIOCGIWNAME, &iwr) == 0)
+			is_wifi = TRUE;
+		close (fd);
+	}
+	return is_wifi;
+}
diff --git a/src/platform/wifi/wifi-utils-wext.h b/src/platform/wifi/wifi-utils-wext.h
new file mode 100644
index 00000000..96ad74eb
--- /dev/null
+++ b/src/platform/wifi/wifi-utils-wext.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ */
+
+#ifndef WIFI_UTILS_WEXT_H
+#define WIFI_UTILS_WEXT_H
+
+#include "wifi-utils.h"
+
+WifiData *wifi_wext_init (const char *iface, int ifindex, gboolean check_scan);
+
+gboolean wifi_wext_is_wifi (const char *iface);
+
+#endif  /* WIFI_UTILS_WEXT_H */
diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c
new file mode 100644
index 00000000..ff480275
--- /dev/null
+++ b/src/platform/wifi/wifi-utils.c
@@ -0,0 +1,225 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2005 - 2011 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <config.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#include "wifi-utils.h"
+#include "wifi-utils-private.h"
+#include "wifi-utils-nl80211.h"
+#if HAVE_WEXT
+#include "wifi-utils-wext.h"
+#endif
+
+gpointer
+wifi_data_new (const char *iface, int ifindex, gsize len)
+{
+	WifiData *data;
+
+	data = g_malloc0 (len);
+	data->iface = g_strdup (iface);
+	data->ifindex = ifindex;
+	return data;
+}
+
+void
+wifi_data_free (WifiData *data)
+{
+	g_free (data->iface);
+	memset (data, 0, sizeof (*data));
+	g_free (data);
+}
+
+/***************************************************************/
+
+WifiData *
+wifi_utils_init (const char *iface, int ifindex, gboolean check_scan)
+{
+	WifiData *ret;
+
+	g_return_val_if_fail (iface != NULL, NULL);
+	g_return_val_if_fail (ifindex > 0, NULL);
+
+	ret = wifi_nl80211_init (iface, ifindex);
+	if (ret == NULL) {
+#if HAVE_WEXT
+		ret = wifi_wext_init (iface, ifindex, check_scan);
+#endif
+	}
+	return ret;
+}
+
+NMDeviceWifiCapabilities
+wifi_utils_get_caps (WifiData *data)
+{
+	g_return_val_if_fail (data != NULL, NM_WIFI_DEVICE_CAP_NONE);
+
+	return data->caps;	
+}
+
+NM80211Mode
+wifi_utils_get_mode (WifiData *data)
+{
+	g_return_val_if_fail (data != NULL, NM_802_11_MODE_UNKNOWN);
+	return data->get_mode (data);
+}
+
+gboolean
+wifi_utils_set_mode (WifiData *data, const NM80211Mode mode)
+{
+	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (   (mode == NM_802_11_MODE_INFRA)
+	                      || (mode == NM_802_11_MODE_AP)
+	                      || (mode == NM_802_11_MODE_ADHOC), FALSE);
+
+	/* nl80211 probably doesn't need this */
+	return data->set_mode ? data->set_mode (data, mode) : TRUE;
+}
+
+guint32
+wifi_utils_get_freq (WifiData *data)
+{
+	g_return_val_if_fail (data != NULL, 0);
+	return data->get_freq (data);
+}
+
+guint32
+wifi_utils_find_freq (WifiData *data, const guint32 *freqs)
+{
+	g_return_val_if_fail (data != NULL, 0);
+	g_return_val_if_fail (freqs != NULL, 0);
+	return data->find_freq (data, freqs);
+}
+
+GByteArray *
+wifi_utils_get_ssid (WifiData *data)
+{
+	g_return_val_if_fail (data != NULL, NULL);
+	return data->get_ssid (data);
+}
+
+gboolean
+wifi_utils_get_bssid (WifiData *data, struct ether_addr *out_bssid)
+{
+	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (out_bssid != NULL, FALSE);
+
+	memset (out_bssid, 0, sizeof (*out_bssid));
+	return data->get_bssid (data, out_bssid);
+}
+
+guint32
+wifi_utils_get_rate (WifiData *data)
+{
+	g_return_val_if_fail (data != NULL, 0);
+	return data->get_rate (data);
+}
+
+int
+wifi_utils_get_qual (WifiData *data)
+{
+	g_return_val_if_fail (data != NULL, 0);
+	return data->get_qual (data);
+}
+
+gboolean
+wifi_utils_get_wowlan (WifiData *data)
+{
+	g_return_val_if_fail (data != NULL, 0);
+	if (!data->get_wowlan)
+		return FALSE;
+	return data->get_wowlan (data);
+}
+
+void
+wifi_utils_deinit (WifiData *data)
+{
+	g_return_if_fail (data != NULL);
+	data->deinit (data);
+	wifi_data_free (data);
+}
+
+gboolean
+wifi_utils_is_wifi (const char *iface, const char *sysfs_path)
+{
+	char phy80211_path[255];
+	struct stat s;
+
+	g_return_val_if_fail (iface != NULL, FALSE);
+
+	if (sysfs_path) {
+		/* Check for nl80211 sysfs paths */
+		g_snprintf (phy80211_path, sizeof (phy80211_path), "%s/phy80211", sysfs_path);
+		if ((stat (phy80211_path, &s) == 0 && (s.st_mode & S_IFDIR)))
+			return TRUE;
+	}
+
+	if (wifi_nl80211_is_wifi (iface))
+		return TRUE;
+
+#if HAVE_WEXT
+	if (wifi_wext_is_wifi (iface))
+		return TRUE;
+#endif
+
+	return FALSE;
+}
+
+
+/* OLPC Mesh-only functions */
+
+guint32
+wifi_utils_get_mesh_channel (WifiData *data)
+{
+	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (data->get_mesh_channel != NULL, FALSE);
+	return data->get_mesh_channel (data);
+}
+
+gboolean
+wifi_utils_set_mesh_channel (WifiData *data, guint32 channel)
+{
+	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (channel <= 13, FALSE);
+	g_return_val_if_fail (data->set_mesh_channel != NULL, FALSE);
+	return data->set_mesh_channel (data, channel);
+}
+
+gboolean
+wifi_utils_set_mesh_ssid (WifiData *data, const GByteArray *ssid)
+{
+	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (data->set_mesh_ssid != NULL, FALSE);
+	return data->set_mesh_ssid (data, ssid);
+}
+
+gboolean
+wifi_utils_indicate_addressing_running (WifiData *data, gboolean running)
+{
+	g_return_val_if_fail (data != NULL, FALSE);
+	if (data->indicate_addressing_running)
+		return data->indicate_addressing_running (data, running);
+	return FALSE;
+}
+
diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h
new file mode 100644
index 00000000..455e075f
--- /dev/null
+++ b/src/platform/wifi/wifi-utils.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2005 - 2011 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef WIFI_UTILS_H
+#define WIFI_UTILS_H
+
+#include <net/ethernet.h>
+#include <glib.h>
+
+#include "NetworkManager.h"
+
+typedef struct WifiData WifiData;
+
+gboolean wifi_utils_is_wifi (const char *iface, const char *sysfs_path);
+
+WifiData *wifi_utils_init (const char *iface, int ifindex, gboolean check_scan);
+
+void wifi_utils_deinit (WifiData *data);
+
+NMDeviceWifiCapabilities wifi_utils_get_caps (WifiData *data);
+
+NM80211Mode wifi_utils_get_mode (WifiData *data);
+
+gboolean wifi_utils_set_mode (WifiData *data, const NM80211Mode mode);
+
+/* Returns frequency in MHz */
+guint32 wifi_utils_get_freq (WifiData *data);
+
+/* Return the first supported frequency in the zero-terminated list */
+guint32 wifi_utils_find_freq (WifiData *data, const guint32 *freqs);
+
+/* Caller must free returned byte array */
+GByteArray *wifi_utils_get_ssid (WifiData *data);
+
+/* Caller must free returned byte array */
+gboolean wifi_utils_get_bssid (WifiData *data, struct ether_addr *out_bssid);
+
+/* Returns current bitrate in Kbps */
+guint32 wifi_utils_get_rate (WifiData *data);
+
+/* Returns quality 0 - 100% on succes, or -1 on error */
+int wifi_utils_get_qual (WifiData *data);
+
+/* Tells the driver DHCP or SLAAC is running */
+gboolean wifi_utils_indicate_addressing_running (WifiData *data, gboolean running);
+
+/* Returns true if WoWLAN is enabled on device */
+gboolean wifi_utils_get_wowlan (WifiData *data);
+
+
+/* OLPC Mesh-only functions */
+guint32 wifi_utils_get_mesh_channel (WifiData *data);
+
+gboolean wifi_utils_set_mesh_channel (WifiData *data, guint32 channel);
+
+gboolean wifi_utils_set_mesh_ssid (WifiData *data, const GByteArray *ssid);
+
+#endif  /* WIFI_UTILS_H */