about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2010-11-29 00:47:11 +0100
committerMichael Biebl <biebl@debian.org>2010-11-29 00:47:11 +0100
commit9fae01fa351805b2903e535736e06971bc0b925e (patch)
treec19bccf84c9f894e50fae678ba6ee2b2044e2d30 /src
Imported Upstream version 0.8.1 upstream/0.8.1
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am278
-rw-r--r--src/Makefile.in1722
-rw-r--r--src/NetworkManager.conf82
-rw-r--r--src/NetworkManagerUtils.c625
-rw-r--r--src/NetworkManagerUtils.h80
-rw-r--r--src/backends/Makefile.am70
-rw-r--r--src/backends/Makefile.in689
-rw-r--r--src/backends/NetworkManagerArch.c70
-rw-r--r--src/backends/NetworkManagerDebian.c64
-rw-r--r--src/backends/NetworkManagerFrugalware.c58
-rw-r--r--src/backends/NetworkManagerGeneric.c113
-rw-r--r--src/backends/NetworkManagerGeneric.h30
-rw-r--r--src/backends/NetworkManagerGentoo.c66
-rw-r--r--src/backends/NetworkManagerMandriva.c61
-rw-r--r--src/backends/NetworkManagerPaldo.c61
-rw-r--r--src/backends/NetworkManagerPardus.c57
-rw-r--r--src/backends/NetworkManagerRedHat.c59
-rw-r--r--src/backends/NetworkManagerSlackware.c57
-rw-r--r--src/backends/NetworkManagerSuSE.c62
-rw-r--r--src/bluez-manager/Makefile.am38
-rw-r--r--src/bluez-manager/Makefile.in601
-rw-r--r--src/bluez-manager/nm-bluez-adapter.c367
-rw-r--r--src/bluez-manager/nm-bluez-adapter.h67
-rw-r--r--src/bluez-manager/nm-bluez-common.h34
-rw-r--r--src/bluez-manager/nm-bluez-device.c452
-rw-r--r--src/bluez-manager/nm-bluez-device.h75
-rw-r--r--src/bluez-manager/nm-bluez-manager.c369
-rw-r--r--src/bluez-manager/nm-bluez-manager.h63
-rw-r--r--src/dhcp-manager/Makefile.am39
-rw-r--r--src/dhcp-manager/Makefile.in610
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c1243
-rw-r--r--src/dhcp-manager/nm-dhcp-client.h142
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c786
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.h49
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.c294
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.h49
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c647
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.h98
-rw-r--r--src/dnsmasq-manager/Makefile.am19
-rw-r--r--src/dnsmasq-manager/Makefile.in564
-rw-r--r--src/dnsmasq-manager/nm-dnsmasq-manager.c495
-rw-r--r--src/dnsmasq-manager/nm-dnsmasq-manager.h69
-rw-r--r--src/gdb-cmd3
-rw-r--r--src/ip6-manager/Makefile.am26
-rw-r--r--src/ip6-manager/Makefile.in570
-rw-r--r--src/ip6-manager/nm-ip6-manager.c1123
-rw-r--r--src/ip6-manager/nm-ip6-manager.h86
-rw-r--r--src/logging/Makefile.am15
-rw-r--r--src/logging/Makefile.in556
-rw-r--r--src/logging/nm-logging.c377
-rw-r--r--src/logging/nm-logging.h111
-rw-r--r--src/main.c758
-rw-r--r--src/modem-manager/Makefile.am35
-rw-r--r--src/modem-manager/Makefile.in611
-rw-r--r--src/modem-manager/nm-modem-cdma.c351
-rw-r--r--src/modem-manager/nm-modem-cdma.h56
-rw-r--r--src/modem-manager/nm-modem-gsm.c554
-rw-r--r--src/modem-manager/nm-modem-gsm.h56
-rw-r--r--src/modem-manager/nm-modem-manager.c443
-rw-r--r--src/modem-manager/nm-modem-manager.h53
-rw-r--r--src/modem-manager/nm-modem-types.h111
-rw-r--r--src/modem-manager/nm-modem.c1147
-rw-r--r--src/modem-manager/nm-modem.h157
-rw-r--r--src/named-manager/Makefile.am21
-rw-r--r--src/named-manager/Makefile.in564
-rw-r--r--src/named-manager/nm-named-manager.c821
-rw-r--r--src/named-manager/nm-named-manager.h92
-rw-r--r--src/nm-activation-request.c700
-rw-r--r--src/nm-activation-request.h103
-rw-r--r--src/nm-active-connection.c63
-rw-r--r--src/nm-active-connection.h42
-rw-r--r--src/nm-call-store.c209
-rw-r--r--src/nm-call-store.h47
-rw-r--r--src/nm-crash-logger.c86
-rw-r--r--src/nm-dbus-manager.c378
-rw-r--r--src/nm-dbus-manager.h78
-rw-r--r--src/nm-device-bt.c1073
-rw-r--r--src/nm-device-bt.h72
-rw-r--r--src/nm-device-cdma.c75
-rw-r--r--src/nm-device-cdma.h55
-rw-r--r--src/nm-device-ethernet.c1975
-rw-r--r--src/nm-device-ethernet.h67
-rw-r--r--src/nm-device-gsm.c75
-rw-r--r--src/nm-device-gsm.h55
-rw-r--r--src/nm-device-interface.c402
-rw-r--r--src/nm-device-interface.h148
-rw-r--r--src/nm-device-modem.c471
-rw-r--r--src/nm-device-modem.h56
-rw-r--r--src/nm-device-olpc-mesh.c974
-rw-r--r--src/nm-device-olpc-mesh.h81
-rw-r--r--src/nm-device-private.h45
-rw-r--r--src/nm-device-wifi.c3936
-rw-r--r--src/nm-device-wifi.h107
-rw-r--r--src/nm-device.c3827
-rw-r--r--src/nm-device.h188
-rw-r--r--src/nm-dhcp4-config.c192
-rw-r--r--src/nm-dhcp4-config.h61
-rw-r--r--src/nm-dhcp6-config.c192
-rw-r--r--src/nm-dhcp6-config.h61
-rw-r--r--src/nm-hostname-provider.c54
-rw-r--r--src/nm-hostname-provider.h44
-rw-r--r--src/nm-ip4-config.c909
-rw-r--r--src/nm-ip4-config.h142
-rw-r--r--src/nm-ip6-config.c745
-rw-r--r--src/nm-ip6-config.h120
-rw-r--r--src/nm-manager.c3463
-rw-r--r--src/nm-manager.h121
-rw-r--r--src/nm-netlink-monitor.c866
-rw-r--r--src/nm-netlink-monitor.h99
-rw-r--r--src/nm-policy-hostname.c205
-rw-r--r--src/nm-policy-hostname.h47
-rw-r--r--src/nm-policy-hosts.c235
-rw-r--r--src/nm-policy-hosts.h40
-rw-r--r--src/nm-policy.c1138
-rw-r--r--src/nm-policy.h36
-rw-r--r--src/nm-properties-changed-signal.c201
-rw-r--r--src/nm-properties-changed-signal.h32
-rw-r--r--src/nm-rfkill.h45
-rw-r--r--src/nm-secrets-provider-interface.c224
-rw-r--r--src/nm-secrets-provider-interface.h90
-rw-r--r--src/nm-system.c1347
-rw-r--r--src/nm-system.h84
-rw-r--r--src/nm-udev-manager.c578
-rw-r--r--src/nm-udev-manager.h72
-rw-r--r--src/nm-wifi-ap.c1477
-rw-r--r--src/nm-wifi-ap.h129
-rw-r--r--src/ppp-manager/Makefile.am58
-rw-r--r--src/ppp-manager/Makefile.in679
-rw-r--r--src/ppp-manager/nm-ppp-manager.c1058
-rw-r--r--src/ppp-manager/nm-ppp-manager.h80
-rw-r--r--src/ppp-manager/nm-ppp-status.h43
-rw-r--r--src/ppp-manager/nm-pppd-plugin.c359
-rw-r--r--src/ppp-manager/nm-pppd-plugin.h29
-rw-r--r--src/supplicant-manager/Makefile.am35
-rw-r--r--src/supplicant-manager/Makefile.in760
-rw-r--r--src/supplicant-manager/nm-supplicant-config.c1014
-rw-r--r--src/supplicant-manager/nm-supplicant-config.h84
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.c1429
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.h145
-rw-r--r--src/supplicant-manager/nm-supplicant-manager.c343
-rw-r--r--src/supplicant-manager/nm-supplicant-manager.h88
-rw-r--r--src/supplicant-manager/nm-supplicant-settings-verify.c238
-rw-r--r--src/supplicant-manager/nm-supplicant-settings-verify.h37
-rw-r--r--src/supplicant-manager/nm-supplicant-types.h28
-rw-r--r--src/supplicant-manager/tests/Makefile.am27
-rw-r--r--src/supplicant-manager/tests/Makefile.in579
-rw-r--r--src/supplicant-manager/tests/test-supplicant-config.c518
-rw-r--r--src/system-settings/Makefile.am61
-rw-r--r--src/system-settings/Makefile.in661
-rw-r--r--src/system-settings/nm-default-wired-connection.c308
-rw-r--r--src/system-settings/nm-default-wired-connection.h59
-rw-r--r--src/system-settings/nm-inotify-helper.c212
-rw-r--r--src/system-settings/nm-inotify-helper.h54
-rw-r--r--src/system-settings/nm-polkit-helpers.h32
-rw-r--r--src/system-settings/nm-sysconfig-connection.c658
-rw-r--r--src/system-settings/nm-sysconfig-connection.h56
-rw-r--r--src/system-settings/nm-sysconfig-settings.c1520
-rw-r--r--src/system-settings/nm-sysconfig-settings.h71
-rw-r--r--src/system-settings/nm-system-config-error.c59
-rw-r--r--src/system-settings/nm-system-config-error.h45
-rw-r--r--src/system-settings/nm-system-config-interface.c163
-rw-r--r--src/system-settings/nm-system-config-interface.h145
-rw-r--r--src/tests/Makefile.am49
-rw-r--r--src/tests/Makefile.in628
-rw-r--r--src/tests/test-dhcp-options.c715
-rw-r--r--src/tests/test-policy-hosts.c394
-rw-r--r--src/vpn-manager/Makefile.am46
-rw-r--r--src/vpn-manager/Makefile.in611
-rw-r--r--src/vpn-manager/nm-vpn-connection.c1192
-rw-r--r--src/vpn-manager/nm-vpn-connection.h78
-rw-r--r--src/vpn-manager/nm-vpn-manager.c369
-rw-r--r--src/vpn-manager/nm-vpn-manager.h86
-rw-r--r--src/vpn-manager/nm-vpn-service.c478
-rw-r--r--src/vpn-manager/nm-vpn-service.h60
-rw-r--r--src/wpa.c455
-rw-r--r--src/wpa.h38
176 files changed, 65542 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 00000000..5d2db6e3
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,278 @@
+SUBDIRS= \
+	logging \
+	named-manager \
+	vpn-manager \
+	dhcp-manager \
+	ip6-manager \
+	supplicant-manager \
+	ppp-manager \
+	backends \
+	dnsmasq-manager \
+	modem-manager \
+	bluez-manager \
+	system-settings \
+	. \
+	tests
+
+INCLUDES = -I${top_srcdir}                   \
+           -I${top_srcdir}/include           \
+           -I${top_builddir}/marshallers   \
+           -I${top_srcdir}/src/logging \
+           -I${top_srcdir}/src/named-manager \
+           -I${top_srcdir}/src/vpn-manager   \
+           -I${top_srcdir}/src/dhcp-manager  \
+           -I${top_srcdir}/src/ip6-manager  \
+           -I${top_srcdir}/src/supplicant-manager  \
+           -I${top_srcdir}/src/dnsmasq-manager  \
+           -I${top_srcdir}/src/modem-manager  \
+           -I$(top_srcdir)/src/bluez-manager \
+           -I$(top_srcdir)/src/system-settings \
+           -I${top_srcdir}/libnm-util \
+           -I${top_srcdir}/libnm-glib \
+           -I${top_srcdir}/callouts
+
+###########################################
+# Test libraries
+###########################################
+
+noinst_LTLIBRARIES = libtest-dhcp.la libtest-policy-hosts.la
+
+###########################################
+# DHCP test library
+###########################################
+
+libtest_dhcp_la_SOURCES = \
+	nm-ip4-config.c \
+	nm-ip6-config.c \
+	nm-hostname-provider.c \
+	nm-dbus-manager.c
+
+libtest_dhcp_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	$(LIBNL_CFLAGS)
+
+libtest_dhcp_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS) \
+	$(LIBNL_LIBS)
+
+###########################################
+# Hosts policy test library
+###########################################
+
+libtest_policy_hosts_la_SOURCES = \
+	nm-policy-hosts.c \
+	nm-policy-hosts.h
+
+libtest_policy_hosts_la_CPPFLAGS = \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	$(GLIB_CFLAGS)
+
+libtest_policy_hosts_la_LIBADD = \
+	${top_builddir}/src/logging/libnm-logging.la \
+	$(GLIB_LIBS)
+
+
+###########################################
+# NetworkManager
+###########################################
+
+sbin_PROGRAMS = NetworkManager
+
+NetworkManager_SOURCES = \
+		nm-call-store.c \
+		nm-call-store.h \
+		nm-device.c \
+		nm-device.h \
+		nm-device-interface.c \
+		nm-device-interface.h \
+		nm-device-private.h \
+		nm-device-ethernet.c \
+		nm-device-ethernet.h \
+		nm-device-wifi.c \
+		nm-device-wifi.h \
+		nm-device-olpc-mesh.c	\
+		nm-device-olpc-mesh.h	\
+		nm-device-bt.c \
+		nm-device-bt.h \
+		nm-device-modem.h \
+		nm-device-modem.c \
+		nm-device-cdma.c \
+		nm-device-cdma.h \
+		nm-device-gsm.c \
+		nm-device-gsm.h \
+		nm-wifi-ap.c \
+		nm-wifi-ap.h \
+		nm-dbus-manager.h \
+		nm-dbus-manager.c \
+		nm-udev-manager.c \
+		nm-udev-manager.h \
+		nm-hostname-provider.c \
+		nm-hostname-provider.h \
+		nm-ip4-config.c \
+		nm-ip4-config.h \
+		nm-ip6-config.c \
+		nm-ip6-config.h \
+		nm-secrets-provider-interface.c \
+		nm-secrets-provider-interface.h \
+		nm-active-connection.h \
+		nm-active-connection.c \
+		main.c \
+		nm-policy.c \
+		nm-policy.h \
+		nm-policy-hosts.c \
+		nm-policy-hosts.h \
+		nm-policy-hostname.c \
+		nm-policy-hostname.h \
+		NetworkManagerUtils.c \
+		NetworkManagerUtils.h \
+		nm-system.c \
+		nm-system.h \
+		nm-manager.c \
+		nm-manager.h \
+		nm-netlink-monitor.c \
+		nm-netlink-monitor.h \
+		nm-activation-request.c \
+		nm-activation-request.h \
+		nm-properties-changed-signal.c \
+		nm-properties-changed-signal.h \
+		wpa.c \
+		wpa.h \
+		nm-dhcp4-config.c \
+		nm-dhcp4-config.h \
+		nm-dhcp6-config.c \
+		nm-dhcp6-config.h \
+		nm-rfkill.h
+
+nm-access-point-glue.h: $(top_srcdir)/introspection/nm-access-point.xml
+	dbus-binding-tool --prefix=nm_access_point --mode=glib-server --output=$@ $<
+
+nm-manager-glue.h: $(top_srcdir)/introspection/nm-manager.xml
+	dbus-binding-tool --prefix=nm_manager --mode=glib-server --output=$@ $<
+
+nm-device-interface-glue.h: $(top_srcdir)/introspection/nm-device.xml
+	dbus-binding-tool --prefix=nm_device_interface --mode=glib-server --output=$@ $<
+
+nm-device-ethernet-glue.h: $(top_srcdir)/introspection/nm-device-ethernet.xml
+	dbus-binding-tool --prefix=nm_device_ethernet --mode=glib-server --output=$@ $<
+
+nm-device-wifi-glue.h: $(top_srcdir)/introspection/nm-device-wifi.xml
+	dbus-binding-tool --prefix=nm_device_wifi --mode=glib-server --output=$@ $<
+
+nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml
+	dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $<
+
+nm-device-olpc-mesh-glue.h: $(top_srcdir)/introspection/nm-device-olpc-mesh.xml
+	dbus-binding-tool --prefix=nm_device_olpc_mesh --mode=glib-server --output=$@ $<
+
+nm-ip4-config-glue.h: $(top_srcdir)/introspection/nm-ip4-config.xml
+	dbus-binding-tool --prefix=nm_ip4_config --mode=glib-server --output=$@ $<
+
+nm-ip6-config-glue.h: $(top_srcdir)/introspection/nm-ip6-config.xml
+	dbus-binding-tool --prefix=nm_ip6_config --mode=glib-server --output=$@ $<
+
+nm-active-connection-glue.h: $(top_srcdir)/introspection/nm-active-connection.xml
+	dbus-binding-tool --prefix=nm_active_connection --mode=glib-server --output=$@ $<
+
+nm-dhcp4-config-glue.h: $(top_srcdir)/introspection/nm-dhcp4-config.xml
+	dbus-binding-tool --prefix=nm_dhcp4_config --mode=glib-server --output=$@ $<
+
+nm-dhcp6-config-glue.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml
+	dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-server --output=$@ $<
+
+nm-device-cdma-glue.h: $(top_srcdir)/introspection/nm-device-cdma.xml
+	dbus-binding-tool --prefix=nm_device_cdma --mode=glib-server --output=$@ $<
+
+nm-device-gsm-glue.h: $(top_srcdir)/introspection/nm-device-gsm.xml
+	dbus-binding-tool --prefix=nm_device_gsm --mode=glib-server --output=$@ $<
+
+BUILT_SOURCES = \
+	nm-access-point-glue.h \
+	nm-manager-glue.h \
+	nm-device-interface-glue.h \
+	nm-device-ethernet-glue.h \
+	nm-device-wifi-glue.h \
+	nm-device-olpc-mesh-glue.h \
+	nm-device-bt-glue.h \
+	nm-device-cdma-glue.h \
+	nm-device-gsm-glue.h \
+	nm-ip4-config-glue.h \
+	nm-ip6-config-glue.h \
+	nm-active-connection-glue.h \
+	nm-dhcp4-config-glue.h \
+	nm-dhcp6-config-glue.h
+
+NetworkManager_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(GUDEV_CFLAGS) \
+	$(LIBNL_CFLAGS) \
+	$(GMODULE_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DSBINDIR=\"$(sbindir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
+	-DNM_RUN_DIR=\"$(rundir)\" \
+	-DNMLOCALEDIR=\"$(datadir)/locale\" \
+	-DARP_DEBUG
+
+NetworkManager_LDADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	./logging/libnm-logging.la \
+	./named-manager/libnamed-manager.la \
+	./vpn-manager/libvpn-manager.la \
+	./dhcp-manager/libdhcp-manager.la \
+	./ip6-manager/libip6-manager.la \
+	./supplicant-manager/libsupplicant-manager.la \
+	./dnsmasq-manager/libdnsmasq-manager.la \
+	./ppp-manager/libppp-manager.la \
+	./modem-manager/libmodem-manager.la \
+	./bluez-manager/libbluez-manager.la \
+	./system-settings/libsystem-settings.la \
+	./backends/libnmbackend.la \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS) \
+	$(GUDEV_LIBS) \
+	$(LIBNL_LIBS) \
+	$(GMODULE_LIBS) \
+	$(LIBM) \
+	$(LIBDL)
+
+NetworkManager_LDFLAGS = -rdynamic
+
+libexec_PROGRAMS = nm-crash-logger
+nm_crash_logger_SOURCES = nm-crash-logger.c
+nm_crash_logger_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DSBINDIR=\"$(sbindir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\"
+nm_crash_logger_LDADD = $(GLIB_LIBS)
+
+
+NetworkManagerdir = $(datadir)/NetworkManager
+NetworkManager_DATA = gdb-cmd
+
+dbusservicedir = $(DBUS_SYS_DIR)
+dbusservice_DATA = NetworkManager.conf
+
+EXTRA_DIST = \
+	$(dbusservice_DATA) \
+	$(NetworkManager_DATA)
+
+rundir=$(localstatedir)/run/NetworkManager
+install-data-hook:
+	$(mkinstalldirs) -m 0700 $(DESTDIR)$(rundir)
+
+CLEANFILES = $(BUILT_SOURCES)
+
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 00000000..7943a100
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,1722 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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@
+sbin_PROGRAMS = NetworkManager$(EXEEXT)
+libexec_PROGRAMS = nm-crash-logger$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libtest_dhcp_la_DEPENDENCIES =  \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libtest_dhcp_la_OBJECTS = libtest_dhcp_la-nm-ip4-config.lo \
+	libtest_dhcp_la-nm-ip6-config.lo \
+	libtest_dhcp_la-nm-hostname-provider.lo \
+	libtest_dhcp_la-nm-dbus-manager.lo
+libtest_dhcp_la_OBJECTS = $(am_libtest_dhcp_la_OBJECTS)
+libtest_policy_hosts_la_DEPENDENCIES =  \
+	${top_builddir}/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1)
+am_libtest_policy_hosts_la_OBJECTS =  \
+	libtest_policy_hosts_la-nm-policy-hosts.lo
+libtest_policy_hosts_la_OBJECTS =  \
+	$(am_libtest_policy_hosts_la_OBJECTS)
+am__installdirs = "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" \
+	"$(DESTDIR)$(NetworkManagerdir)" "$(DESTDIR)$(dbusservicedir)"
+PROGRAMS = $(libexec_PROGRAMS) $(sbin_PROGRAMS)
+am_NetworkManager_OBJECTS = NetworkManager-nm-call-store.$(OBJEXT) \
+	NetworkManager-nm-device.$(OBJEXT) \
+	NetworkManager-nm-device-interface.$(OBJEXT) \
+	NetworkManager-nm-device-ethernet.$(OBJEXT) \
+	NetworkManager-nm-device-wifi.$(OBJEXT) \
+	NetworkManager-nm-device-olpc-mesh.$(OBJEXT) \
+	NetworkManager-nm-device-bt.$(OBJEXT) \
+	NetworkManager-nm-device-modem.$(OBJEXT) \
+	NetworkManager-nm-device-cdma.$(OBJEXT) \
+	NetworkManager-nm-device-gsm.$(OBJEXT) \
+	NetworkManager-nm-wifi-ap.$(OBJEXT) \
+	NetworkManager-nm-dbus-manager.$(OBJEXT) \
+	NetworkManager-nm-udev-manager.$(OBJEXT) \
+	NetworkManager-nm-hostname-provider.$(OBJEXT) \
+	NetworkManager-nm-ip4-config.$(OBJEXT) \
+	NetworkManager-nm-ip6-config.$(OBJEXT) \
+	NetworkManager-nm-secrets-provider-interface.$(OBJEXT) \
+	NetworkManager-nm-active-connection.$(OBJEXT) \
+	NetworkManager-main.$(OBJEXT) \
+	NetworkManager-nm-policy.$(OBJEXT) \
+	NetworkManager-nm-policy-hosts.$(OBJEXT) \
+	NetworkManager-nm-policy-hostname.$(OBJEXT) \
+	NetworkManager-NetworkManagerUtils.$(OBJEXT) \
+	NetworkManager-nm-system.$(OBJEXT) \
+	NetworkManager-nm-manager.$(OBJEXT) \
+	NetworkManager-nm-netlink-monitor.$(OBJEXT) \
+	NetworkManager-nm-activation-request.$(OBJEXT) \
+	NetworkManager-nm-properties-changed-signal.$(OBJEXT) \
+	NetworkManager-wpa.$(OBJEXT) \
+	NetworkManager-nm-dhcp4-config.$(OBJEXT) \
+	NetworkManager-nm-dhcp6-config.$(OBJEXT)
+NetworkManager_OBJECTS = $(am_NetworkManager_OBJECTS)
+NetworkManager_DEPENDENCIES =  \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	./logging/libnm-logging.la ./named-manager/libnamed-manager.la \
+	./vpn-manager/libvpn-manager.la \
+	./dhcp-manager/libdhcp-manager.la \
+	./ip6-manager/libip6-manager.la \
+	./supplicant-manager/libsupplicant-manager.la \
+	./dnsmasq-manager/libdnsmasq-manager.la \
+	./ppp-manager/libppp-manager.la \
+	./modem-manager/libmodem-manager.la \
+	./bluez-manager/libbluez-manager.la \
+	./system-settings/libsystem-settings.la \
+	./backends/libnmbackend.la \
+	$(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+NetworkManager_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(NetworkManager_LDFLAGS) $(LDFLAGS) -o $@
+am_nm_crash_logger_OBJECTS =  \
+	nm_crash_logger-nm-crash-logger.$(OBJEXT)
+nm_crash_logger_OBJECTS = $(am_nm_crash_logger_OBJECTS)
+nm_crash_logger_DEPENDENCIES = $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libtest_dhcp_la_SOURCES) \
+	$(libtest_policy_hosts_la_SOURCES) $(NetworkManager_SOURCES) \
+	$(nm_crash_logger_SOURCES)
+DIST_SOURCES = $(libtest_dhcp_la_SOURCES) \
+	$(libtest_policy_hosts_la_SOURCES) $(NetworkManager_SOURCES) \
+	$(nm_crash_logger_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-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 uninstall-recursive
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+DATA = $(NetworkManager_DATA) $(dbusservice_DATA)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+	$(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+	distdir
+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@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = \
+	logging \
+	named-manager \
+	vpn-manager \
+	dhcp-manager \
+	ip6-manager \
+	supplicant-manager \
+	ppp-manager \
+	backends \
+	dnsmasq-manager \
+	modem-manager \
+	bluez-manager \
+	system-settings \
+	. \
+	tests
+
+INCLUDES = -I${top_srcdir}                   \
+           -I${top_srcdir}/include           \
+           -I${top_builddir}/marshallers   \
+           -I${top_srcdir}/src/logging \
+           -I${top_srcdir}/src/named-manager \
+           -I${top_srcdir}/src/vpn-manager   \
+           -I${top_srcdir}/src/dhcp-manager  \
+           -I${top_srcdir}/src/ip6-manager  \
+           -I${top_srcdir}/src/supplicant-manager  \
+           -I${top_srcdir}/src/dnsmasq-manager  \
+           -I${top_srcdir}/src/modem-manager  \
+           -I$(top_srcdir)/src/bluez-manager \
+           -I$(top_srcdir)/src/system-settings \
+           -I${top_srcdir}/libnm-util \
+           -I${top_srcdir}/libnm-glib \
+           -I${top_srcdir}/callouts
+
+
+###########################################
+# Test libraries
+###########################################
+noinst_LTLIBRARIES = libtest-dhcp.la libtest-policy-hosts.la
+
+###########################################
+# DHCP test library
+###########################################
+libtest_dhcp_la_SOURCES = \
+	nm-ip4-config.c \
+	nm-ip6-config.c \
+	nm-hostname-provider.c \
+	nm-dbus-manager.c
+
+libtest_dhcp_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	$(LIBNL_CFLAGS)
+
+libtest_dhcp_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS) \
+	$(LIBNL_LIBS)
+
+
+###########################################
+# Hosts policy test library
+###########################################
+libtest_policy_hosts_la_SOURCES = \
+	nm-policy-hosts.c \
+	nm-policy-hosts.h
+
+libtest_policy_hosts_la_CPPFLAGS = \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	$(GLIB_CFLAGS)
+
+libtest_policy_hosts_la_LIBADD = \
+	${top_builddir}/src/logging/libnm-logging.la \
+	$(GLIB_LIBS)
+
+NetworkManager_SOURCES = \
+		nm-call-store.c \
+		nm-call-store.h \
+		nm-device.c \
+		nm-device.h \
+		nm-device-interface.c \
+		nm-device-interface.h \
+		nm-device-private.h \
+		nm-device-ethernet.c \
+		nm-device-ethernet.h \
+		nm-device-wifi.c \
+		nm-device-wifi.h \
+		nm-device-olpc-mesh.c	\
+		nm-device-olpc-mesh.h	\
+		nm-device-bt.c \
+		nm-device-bt.h \
+		nm-device-modem.h \
+		nm-device-modem.c \
+		nm-device-cdma.c \
+		nm-device-cdma.h \
+		nm-device-gsm.c \
+		nm-device-gsm.h \
+		nm-wifi-ap.c \
+		nm-wifi-ap.h \
+		nm-dbus-manager.h \
+		nm-dbus-manager.c \
+		nm-udev-manager.c \
+		nm-udev-manager.h \
+		nm-hostname-provider.c \
+		nm-hostname-provider.h \
+		nm-ip4-config.c \
+		nm-ip4-config.h \
+		nm-ip6-config.c \
+		nm-ip6-config.h \
+		nm-secrets-provider-interface.c \
+		nm-secrets-provider-interface.h \
+		nm-active-connection.h \
+		nm-active-connection.c \
+		main.c \
+		nm-policy.c \
+		nm-policy.h \
+		nm-policy-hosts.c \
+		nm-policy-hosts.h \
+		nm-policy-hostname.c \
+		nm-policy-hostname.h \
+		NetworkManagerUtils.c \
+		NetworkManagerUtils.h \
+		nm-system.c \
+		nm-system.h \
+		nm-manager.c \
+		nm-manager.h \
+		nm-netlink-monitor.c \
+		nm-netlink-monitor.h \
+		nm-activation-request.c \
+		nm-activation-request.h \
+		nm-properties-changed-signal.c \
+		nm-properties-changed-signal.h \
+		wpa.c \
+		wpa.h \
+		nm-dhcp4-config.c \
+		nm-dhcp4-config.h \
+		nm-dhcp6-config.c \
+		nm-dhcp6-config.h \
+		nm-rfkill.h
+
+BUILT_SOURCES = \
+	nm-access-point-glue.h \
+	nm-manager-glue.h \
+	nm-device-interface-glue.h \
+	nm-device-ethernet-glue.h \
+	nm-device-wifi-glue.h \
+	nm-device-olpc-mesh-glue.h \
+	nm-device-bt-glue.h \
+	nm-device-cdma-glue.h \
+	nm-device-gsm-glue.h \
+	nm-ip4-config-glue.h \
+	nm-ip6-config-glue.h \
+	nm-active-connection-glue.h \
+	nm-dhcp4-config-glue.h \
+	nm-dhcp6-config-glue.h
+
+NetworkManager_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(GUDEV_CFLAGS) \
+	$(LIBNL_CFLAGS) \
+	$(GMODULE_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DSBINDIR=\"$(sbindir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
+	-DNM_RUN_DIR=\"$(rundir)\" \
+	-DNMLOCALEDIR=\"$(datadir)/locale\" \
+	-DARP_DEBUG
+
+NetworkManager_LDADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	./logging/libnm-logging.la \
+	./named-manager/libnamed-manager.la \
+	./vpn-manager/libvpn-manager.la \
+	./dhcp-manager/libdhcp-manager.la \
+	./ip6-manager/libip6-manager.la \
+	./supplicant-manager/libsupplicant-manager.la \
+	./dnsmasq-manager/libdnsmasq-manager.la \
+	./ppp-manager/libppp-manager.la \
+	./modem-manager/libmodem-manager.la \
+	./bluez-manager/libbluez-manager.la \
+	./system-settings/libsystem-settings.la \
+	./backends/libnmbackend.la \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS) \
+	$(GUDEV_LIBS) \
+	$(LIBNL_LIBS) \
+	$(GMODULE_LIBS) \
+	$(LIBM) \
+	$(LIBDL)
+
+NetworkManager_LDFLAGS = -rdynamic
+nm_crash_logger_SOURCES = nm-crash-logger.c
+nm_crash_logger_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DSBINDIR=\"$(sbindir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\"
+
+nm_crash_logger_LDADD = $(GLIB_LIBS)
+NetworkManagerdir = $(datadir)/NetworkManager
+NetworkManager_DATA = gdb-cmd
+dbusservicedir = $(DBUS_SYS_DIR)
+dbusservice_DATA = NetworkManager.conf
+EXTRA_DIST = \
+	$(dbusservice_DATA) \
+	$(NetworkManager_DATA)
+
+rundir = $(localstatedir)/run/NetworkManager
+CLEANFILES = $(BUILT_SOURCES)
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.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/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libtest-dhcp.la: $(libtest_dhcp_la_OBJECTS) $(libtest_dhcp_la_DEPENDENCIES) 
+	$(LINK)  $(libtest_dhcp_la_OBJECTS) $(libtest_dhcp_la_LIBADD) $(LIBS)
+libtest-policy-hosts.la: $(libtest_policy_hosts_la_OBJECTS) $(libtest_policy_hosts_la_DEPENDENCIES) 
+	$(LINK)  $(libtest_policy_hosts_la_OBJECTS) $(libtest_policy_hosts_la_LIBADD) $(LIBS)
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	test -z "$(libexecdir)" || $(MKDIR_P) "$(DESTDIR)$(libexecdir)"
+	@list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p || test -f $$p1; \
+	  then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-libexecPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' `; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+	@list='$(libexec_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
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+	@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p || test -f $$p1; \
+	  then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-sbinPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' `; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+	@list='$(sbin_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
+NetworkManager$(EXEEXT): $(NetworkManager_OBJECTS) $(NetworkManager_DEPENDENCIES) 
+	@rm -f NetworkManager$(EXEEXT)
+	$(NetworkManager_LINK) $(NetworkManager_OBJECTS) $(NetworkManager_LDADD) $(LIBS)
+nm-crash-logger$(EXEEXT): $(nm_crash_logger_OBJECTS) $(nm_crash_logger_DEPENDENCIES) 
+	@rm -f nm-crash-logger$(EXEEXT)
+	$(LINK) $(nm_crash_logger_OBJECTS) $(nm_crash_logger_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-NetworkManagerUtils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-activation-request.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-active-connection.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-call-store.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-dbus-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-bt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-cdma.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-ethernet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-gsm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-interface.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-modem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device-wifi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-dhcp4-config.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-dhcp6-config.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-hostname-provider.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-ip4-config.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-ip6-config.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-netlink-monitor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-policy-hostname.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-policy-hosts.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-policy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-properties-changed-signal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-system.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-udev-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-nm-wifi-ap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManager-wpa.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_dhcp_la-nm-dbus-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_dhcp_la-nm-hostname-provider.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_dhcp_la-nm-ip4-config.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_dhcp_la-nm-ip6-config.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_policy_hosts_la-nm-policy-hosts.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm_crash_logger-nm-crash-logger.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libtest_dhcp_la-nm-ip4-config.lo: nm-ip4-config.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_dhcp_la-nm-ip4-config.lo -MD -MP -MF $(DEPDIR)/libtest_dhcp_la-nm-ip4-config.Tpo -c -o libtest_dhcp_la-nm-ip4-config.lo `test -f 'nm-ip4-config.c' || echo '$(srcdir)/'`nm-ip4-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libtest_dhcp_la-nm-ip4-config.Tpo $(DEPDIR)/libtest_dhcp_la-nm-ip4-config.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-ip4-config.c' object='libtest_dhcp_la-nm-ip4-config.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_dhcp_la-nm-ip4-config.lo `test -f 'nm-ip4-config.c' || echo '$(srcdir)/'`nm-ip4-config.c
+
+libtest_dhcp_la-nm-ip6-config.lo: nm-ip6-config.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_dhcp_la-nm-ip6-config.lo -MD -MP -MF $(DEPDIR)/libtest_dhcp_la-nm-ip6-config.Tpo -c -o libtest_dhcp_la-nm-ip6-config.lo `test -f 'nm-ip6-config.c' || echo '$(srcdir)/'`nm-ip6-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libtest_dhcp_la-nm-ip6-config.Tpo $(DEPDIR)/libtest_dhcp_la-nm-ip6-config.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-ip6-config.c' object='libtest_dhcp_la-nm-ip6-config.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_dhcp_la-nm-ip6-config.lo `test -f 'nm-ip6-config.c' || echo '$(srcdir)/'`nm-ip6-config.c
+
+libtest_dhcp_la-nm-hostname-provider.lo: nm-hostname-provider.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_dhcp_la-nm-hostname-provider.lo -MD -MP -MF $(DEPDIR)/libtest_dhcp_la-nm-hostname-provider.Tpo -c -o libtest_dhcp_la-nm-hostname-provider.lo `test -f 'nm-hostname-provider.c' || echo '$(srcdir)/'`nm-hostname-provider.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libtest_dhcp_la-nm-hostname-provider.Tpo $(DEPDIR)/libtest_dhcp_la-nm-hostname-provider.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-hostname-provider.c' object='libtest_dhcp_la-nm-hostname-provider.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_dhcp_la-nm-hostname-provider.lo `test -f 'nm-hostname-provider.c' || echo '$(srcdir)/'`nm-hostname-provider.c
+
+libtest_dhcp_la-nm-dbus-manager.lo: nm-dbus-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_dhcp_la-nm-dbus-manager.lo -MD -MP -MF $(DEPDIR)/libtest_dhcp_la-nm-dbus-manager.Tpo -c -o libtest_dhcp_la-nm-dbus-manager.lo `test -f 'nm-dbus-manager.c' || echo '$(srcdir)/'`nm-dbus-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libtest_dhcp_la-nm-dbus-manager.Tpo $(DEPDIR)/libtest_dhcp_la-nm-dbus-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dbus-manager.c' object='libtest_dhcp_la-nm-dbus-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_dhcp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_dhcp_la-nm-dbus-manager.lo `test -f 'nm-dbus-manager.c' || echo '$(srcdir)/'`nm-dbus-manager.c
+
+libtest_policy_hosts_la-nm-policy-hosts.lo: nm-policy-hosts.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_policy_hosts_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_policy_hosts_la-nm-policy-hosts.lo -MD -MP -MF $(DEPDIR)/libtest_policy_hosts_la-nm-policy-hosts.Tpo -c -o libtest_policy_hosts_la-nm-policy-hosts.lo `test -f 'nm-policy-hosts.c' || echo '$(srcdir)/'`nm-policy-hosts.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libtest_policy_hosts_la-nm-policy-hosts.Tpo $(DEPDIR)/libtest_policy_hosts_la-nm-policy-hosts.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-policy-hosts.c' object='libtest_policy_hosts_la-nm-policy-hosts.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_policy_hosts_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_policy_hosts_la-nm-policy-hosts.lo `test -f 'nm-policy-hosts.c' || echo '$(srcdir)/'`nm-policy-hosts.c
+
+NetworkManager-nm-call-store.o: nm-call-store.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-call-store.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-call-store.Tpo -c -o NetworkManager-nm-call-store.o `test -f 'nm-call-store.c' || echo '$(srcdir)/'`nm-call-store.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-call-store.Tpo $(DEPDIR)/NetworkManager-nm-call-store.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-call-store.c' object='NetworkManager-nm-call-store.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-call-store.o `test -f 'nm-call-store.c' || echo '$(srcdir)/'`nm-call-store.c
+
+NetworkManager-nm-call-store.obj: nm-call-store.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-call-store.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-call-store.Tpo -c -o NetworkManager-nm-call-store.obj `if test -f 'nm-call-store.c'; then $(CYGPATH_W) 'nm-call-store.c'; else $(CYGPATH_W) '$(srcdir)/nm-call-store.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-call-store.Tpo $(DEPDIR)/NetworkManager-nm-call-store.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-call-store.c' object='NetworkManager-nm-call-store.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-call-store.obj `if test -f 'nm-call-store.c'; then $(CYGPATH_W) 'nm-call-store.c'; else $(CYGPATH_W) '$(srcdir)/nm-call-store.c'; fi`
+
+NetworkManager-nm-device.o: nm-device.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device.Tpo -c -o NetworkManager-nm-device.o `test -f 'nm-device.c' || echo '$(srcdir)/'`nm-device.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device.Tpo $(DEPDIR)/NetworkManager-nm-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device.c' object='NetworkManager-nm-device.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device.o `test -f 'nm-device.c' || echo '$(srcdir)/'`nm-device.c
+
+NetworkManager-nm-device.obj: nm-device.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device.Tpo -c -o NetworkManager-nm-device.obj `if test -f 'nm-device.c'; then $(CYGPATH_W) 'nm-device.c'; else $(CYGPATH_W) '$(srcdir)/nm-device.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device.Tpo $(DEPDIR)/NetworkManager-nm-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device.c' object='NetworkManager-nm-device.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device.obj `if test -f 'nm-device.c'; then $(CYGPATH_W) 'nm-device.c'; else $(CYGPATH_W) '$(srcdir)/nm-device.c'; fi`
+
+NetworkManager-nm-device-interface.o: nm-device-interface.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-interface.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-interface.Tpo -c -o NetworkManager-nm-device-interface.o `test -f 'nm-device-interface.c' || echo '$(srcdir)/'`nm-device-interface.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-interface.Tpo $(DEPDIR)/NetworkManager-nm-device-interface.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-interface.c' object='NetworkManager-nm-device-interface.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-interface.o `test -f 'nm-device-interface.c' || echo '$(srcdir)/'`nm-device-interface.c
+
+NetworkManager-nm-device-interface.obj: nm-device-interface.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-interface.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-interface.Tpo -c -o NetworkManager-nm-device-interface.obj `if test -f 'nm-device-interface.c'; then $(CYGPATH_W) 'nm-device-interface.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-interface.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-interface.Tpo $(DEPDIR)/NetworkManager-nm-device-interface.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-interface.c' object='NetworkManager-nm-device-interface.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-interface.obj `if test -f 'nm-device-interface.c'; then $(CYGPATH_W) 'nm-device-interface.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-interface.c'; fi`
+
+NetworkManager-nm-device-ethernet.o: nm-device-ethernet.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-ethernet.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-ethernet.Tpo -c -o NetworkManager-nm-device-ethernet.o `test -f 'nm-device-ethernet.c' || echo '$(srcdir)/'`nm-device-ethernet.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-ethernet.Tpo $(DEPDIR)/NetworkManager-nm-device-ethernet.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-ethernet.c' object='NetworkManager-nm-device-ethernet.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-ethernet.o `test -f 'nm-device-ethernet.c' || echo '$(srcdir)/'`nm-device-ethernet.c
+
+NetworkManager-nm-device-ethernet.obj: nm-device-ethernet.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-ethernet.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-ethernet.Tpo -c -o NetworkManager-nm-device-ethernet.obj `if test -f 'nm-device-ethernet.c'; then $(CYGPATH_W) 'nm-device-ethernet.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-ethernet.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-ethernet.Tpo $(DEPDIR)/NetworkManager-nm-device-ethernet.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-ethernet.c' object='NetworkManager-nm-device-ethernet.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-ethernet.obj `if test -f 'nm-device-ethernet.c'; then $(CYGPATH_W) 'nm-device-ethernet.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-ethernet.c'; fi`
+
+NetworkManager-nm-device-wifi.o: nm-device-wifi.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-wifi.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-wifi.Tpo -c -o NetworkManager-nm-device-wifi.o `test -f 'nm-device-wifi.c' || echo '$(srcdir)/'`nm-device-wifi.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-wifi.Tpo $(DEPDIR)/NetworkManager-nm-device-wifi.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-wifi.c' object='NetworkManager-nm-device-wifi.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-wifi.o `test -f 'nm-device-wifi.c' || echo '$(srcdir)/'`nm-device-wifi.c
+
+NetworkManager-nm-device-wifi.obj: nm-device-wifi.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-wifi.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-wifi.Tpo -c -o NetworkManager-nm-device-wifi.obj `if test -f 'nm-device-wifi.c'; then $(CYGPATH_W) 'nm-device-wifi.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-wifi.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-wifi.Tpo $(DEPDIR)/NetworkManager-nm-device-wifi.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-wifi.c' object='NetworkManager-nm-device-wifi.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-wifi.obj `if test -f 'nm-device-wifi.c'; then $(CYGPATH_W) 'nm-device-wifi.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-wifi.c'; fi`
+
+NetworkManager-nm-device-olpc-mesh.o: nm-device-olpc-mesh.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-olpc-mesh.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Tpo -c -o NetworkManager-nm-device-olpc-mesh.o `test -f 'nm-device-olpc-mesh.c' || echo '$(srcdir)/'`nm-device-olpc-mesh.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Tpo $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-olpc-mesh.c' object='NetworkManager-nm-device-olpc-mesh.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-olpc-mesh.o `test -f 'nm-device-olpc-mesh.c' || echo '$(srcdir)/'`nm-device-olpc-mesh.c
+
+NetworkManager-nm-device-olpc-mesh.obj: nm-device-olpc-mesh.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-olpc-mesh.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Tpo -c -o NetworkManager-nm-device-olpc-mesh.obj `if test -f 'nm-device-olpc-mesh.c'; then $(CYGPATH_W) 'nm-device-olpc-mesh.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-olpc-mesh.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Tpo $(DEPDIR)/NetworkManager-nm-device-olpc-mesh.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-olpc-mesh.c' object='NetworkManager-nm-device-olpc-mesh.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-olpc-mesh.obj `if test -f 'nm-device-olpc-mesh.c'; then $(CYGPATH_W) 'nm-device-olpc-mesh.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-olpc-mesh.c'; fi`
+
+NetworkManager-nm-device-bt.o: nm-device-bt.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-bt.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-bt.Tpo -c -o NetworkManager-nm-device-bt.o `test -f 'nm-device-bt.c' || echo '$(srcdir)/'`nm-device-bt.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-bt.Tpo $(DEPDIR)/NetworkManager-nm-device-bt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-bt.c' object='NetworkManager-nm-device-bt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-bt.o `test -f 'nm-device-bt.c' || echo '$(srcdir)/'`nm-device-bt.c
+
+NetworkManager-nm-device-bt.obj: nm-device-bt.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-bt.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-bt.Tpo -c -o NetworkManager-nm-device-bt.obj `if test -f 'nm-device-bt.c'; then $(CYGPATH_W) 'nm-device-bt.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-bt.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-bt.Tpo $(DEPDIR)/NetworkManager-nm-device-bt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-bt.c' object='NetworkManager-nm-device-bt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-bt.obj `if test -f 'nm-device-bt.c'; then $(CYGPATH_W) 'nm-device-bt.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-bt.c'; fi`
+
+NetworkManager-nm-device-modem.o: nm-device-modem.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-modem.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-modem.Tpo -c -o NetworkManager-nm-device-modem.o `test -f 'nm-device-modem.c' || echo '$(srcdir)/'`nm-device-modem.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-modem.Tpo $(DEPDIR)/NetworkManager-nm-device-modem.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-modem.c' object='NetworkManager-nm-device-modem.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-modem.o `test -f 'nm-device-modem.c' || echo '$(srcdir)/'`nm-device-modem.c
+
+NetworkManager-nm-device-modem.obj: nm-device-modem.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-modem.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-modem.Tpo -c -o NetworkManager-nm-device-modem.obj `if test -f 'nm-device-modem.c'; then $(CYGPATH_W) 'nm-device-modem.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-modem.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-modem.Tpo $(DEPDIR)/NetworkManager-nm-device-modem.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-modem.c' object='NetworkManager-nm-device-modem.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-modem.obj `if test -f 'nm-device-modem.c'; then $(CYGPATH_W) 'nm-device-modem.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-modem.c'; fi`
+
+NetworkManager-nm-device-cdma.o: nm-device-cdma.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-cdma.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-cdma.Tpo -c -o NetworkManager-nm-device-cdma.o `test -f 'nm-device-cdma.c' || echo '$(srcdir)/'`nm-device-cdma.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-cdma.Tpo $(DEPDIR)/NetworkManager-nm-device-cdma.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-cdma.c' object='NetworkManager-nm-device-cdma.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-cdma.o `test -f 'nm-device-cdma.c' || echo '$(srcdir)/'`nm-device-cdma.c
+
+NetworkManager-nm-device-cdma.obj: nm-device-cdma.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-cdma.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-cdma.Tpo -c -o NetworkManager-nm-device-cdma.obj `if test -f 'nm-device-cdma.c'; then $(CYGPATH_W) 'nm-device-cdma.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-cdma.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-cdma.Tpo $(DEPDIR)/NetworkManager-nm-device-cdma.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-cdma.c' object='NetworkManager-nm-device-cdma.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-cdma.obj `if test -f 'nm-device-cdma.c'; then $(CYGPATH_W) 'nm-device-cdma.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-cdma.c'; fi`
+
+NetworkManager-nm-device-gsm.o: nm-device-gsm.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-gsm.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-gsm.Tpo -c -o NetworkManager-nm-device-gsm.o `test -f 'nm-device-gsm.c' || echo '$(srcdir)/'`nm-device-gsm.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-gsm.Tpo $(DEPDIR)/NetworkManager-nm-device-gsm.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-gsm.c' object='NetworkManager-nm-device-gsm.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-gsm.o `test -f 'nm-device-gsm.c' || echo '$(srcdir)/'`nm-device-gsm.c
+
+NetworkManager-nm-device-gsm.obj: nm-device-gsm.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-device-gsm.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-device-gsm.Tpo -c -o NetworkManager-nm-device-gsm.obj `if test -f 'nm-device-gsm.c'; then $(CYGPATH_W) 'nm-device-gsm.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-gsm.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-device-gsm.Tpo $(DEPDIR)/NetworkManager-nm-device-gsm.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-device-gsm.c' object='NetworkManager-nm-device-gsm.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-device-gsm.obj `if test -f 'nm-device-gsm.c'; then $(CYGPATH_W) 'nm-device-gsm.c'; else $(CYGPATH_W) '$(srcdir)/nm-device-gsm.c'; fi`
+
+NetworkManager-nm-wifi-ap.o: nm-wifi-ap.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-wifi-ap.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-wifi-ap.Tpo -c -o NetworkManager-nm-wifi-ap.o `test -f 'nm-wifi-ap.c' || echo '$(srcdir)/'`nm-wifi-ap.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-wifi-ap.Tpo $(DEPDIR)/NetworkManager-nm-wifi-ap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-wifi-ap.c' object='NetworkManager-nm-wifi-ap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-wifi-ap.o `test -f 'nm-wifi-ap.c' || echo '$(srcdir)/'`nm-wifi-ap.c
+
+NetworkManager-nm-wifi-ap.obj: nm-wifi-ap.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-wifi-ap.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-wifi-ap.Tpo -c -o NetworkManager-nm-wifi-ap.obj `if test -f 'nm-wifi-ap.c'; then $(CYGPATH_W) 'nm-wifi-ap.c'; else $(CYGPATH_W) '$(srcdir)/nm-wifi-ap.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-wifi-ap.Tpo $(DEPDIR)/NetworkManager-nm-wifi-ap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-wifi-ap.c' object='NetworkManager-nm-wifi-ap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-wifi-ap.obj `if test -f 'nm-wifi-ap.c'; then $(CYGPATH_W) 'nm-wifi-ap.c'; else $(CYGPATH_W) '$(srcdir)/nm-wifi-ap.c'; fi`
+
+NetworkManager-nm-dbus-manager.o: nm-dbus-manager.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dbus-manager.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dbus-manager.Tpo -c -o NetworkManager-nm-dbus-manager.o `test -f 'nm-dbus-manager.c' || echo '$(srcdir)/'`nm-dbus-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-dbus-manager.Tpo $(DEPDIR)/NetworkManager-nm-dbus-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dbus-manager.c' object='NetworkManager-nm-dbus-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dbus-manager.o `test -f 'nm-dbus-manager.c' || echo '$(srcdir)/'`nm-dbus-manager.c
+
+NetworkManager-nm-dbus-manager.obj: nm-dbus-manager.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dbus-manager.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dbus-manager.Tpo -c -o NetworkManager-nm-dbus-manager.obj `if test -f 'nm-dbus-manager.c'; then $(CYGPATH_W) 'nm-dbus-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-dbus-manager.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-dbus-manager.Tpo $(DEPDIR)/NetworkManager-nm-dbus-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dbus-manager.c' object='NetworkManager-nm-dbus-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dbus-manager.obj `if test -f 'nm-dbus-manager.c'; then $(CYGPATH_W) 'nm-dbus-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-dbus-manager.c'; fi`
+
+NetworkManager-nm-udev-manager.o: nm-udev-manager.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-udev-manager.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-udev-manager.Tpo -c -o NetworkManager-nm-udev-manager.o `test -f 'nm-udev-manager.c' || echo '$(srcdir)/'`nm-udev-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-udev-manager.Tpo $(DEPDIR)/NetworkManager-nm-udev-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-udev-manager.c' object='NetworkManager-nm-udev-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-udev-manager.o `test -f 'nm-udev-manager.c' || echo '$(srcdir)/'`nm-udev-manager.c
+
+NetworkManager-nm-udev-manager.obj: nm-udev-manager.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-udev-manager.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-udev-manager.Tpo -c -o NetworkManager-nm-udev-manager.obj `if test -f 'nm-udev-manager.c'; then $(CYGPATH_W) 'nm-udev-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-udev-manager.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-udev-manager.Tpo $(DEPDIR)/NetworkManager-nm-udev-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-udev-manager.c' object='NetworkManager-nm-udev-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-udev-manager.obj `if test -f 'nm-udev-manager.c'; then $(CYGPATH_W) 'nm-udev-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-udev-manager.c'; fi`
+
+NetworkManager-nm-hostname-provider.o: nm-hostname-provider.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-hostname-provider.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-hostname-provider.Tpo -c -o NetworkManager-nm-hostname-provider.o `test -f 'nm-hostname-provider.c' || echo '$(srcdir)/'`nm-hostname-provider.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-hostname-provider.Tpo $(DEPDIR)/NetworkManager-nm-hostname-provider.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-hostname-provider.c' object='NetworkManager-nm-hostname-provider.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-hostname-provider.o `test -f 'nm-hostname-provider.c' || echo '$(srcdir)/'`nm-hostname-provider.c
+
+NetworkManager-nm-hostname-provider.obj: nm-hostname-provider.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-hostname-provider.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-hostname-provider.Tpo -c -o NetworkManager-nm-hostname-provider.obj `if test -f 'nm-hostname-provider.c'; then $(CYGPATH_W) 'nm-hostname-provider.c'; else $(CYGPATH_W) '$(srcdir)/nm-hostname-provider.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-hostname-provider.Tpo $(DEPDIR)/NetworkManager-nm-hostname-provider.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-hostname-provider.c' object='NetworkManager-nm-hostname-provider.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-hostname-provider.obj `if test -f 'nm-hostname-provider.c'; then $(CYGPATH_W) 'nm-hostname-provider.c'; else $(CYGPATH_W) '$(srcdir)/nm-hostname-provider.c'; fi`
+
+NetworkManager-nm-ip4-config.o: nm-ip4-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-ip4-config.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-ip4-config.Tpo -c -o NetworkManager-nm-ip4-config.o `test -f 'nm-ip4-config.c' || echo '$(srcdir)/'`nm-ip4-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-ip4-config.Tpo $(DEPDIR)/NetworkManager-nm-ip4-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-ip4-config.c' object='NetworkManager-nm-ip4-config.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-ip4-config.o `test -f 'nm-ip4-config.c' || echo '$(srcdir)/'`nm-ip4-config.c
+
+NetworkManager-nm-ip4-config.obj: nm-ip4-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-ip4-config.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-ip4-config.Tpo -c -o NetworkManager-nm-ip4-config.obj `if test -f 'nm-ip4-config.c'; then $(CYGPATH_W) 'nm-ip4-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-ip4-config.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-ip4-config.Tpo $(DEPDIR)/NetworkManager-nm-ip4-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-ip4-config.c' object='NetworkManager-nm-ip4-config.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-ip4-config.obj `if test -f 'nm-ip4-config.c'; then $(CYGPATH_W) 'nm-ip4-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-ip4-config.c'; fi`
+
+NetworkManager-nm-ip6-config.o: nm-ip6-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-ip6-config.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-ip6-config.Tpo -c -o NetworkManager-nm-ip6-config.o `test -f 'nm-ip6-config.c' || echo '$(srcdir)/'`nm-ip6-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-ip6-config.Tpo $(DEPDIR)/NetworkManager-nm-ip6-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-ip6-config.c' object='NetworkManager-nm-ip6-config.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-ip6-config.o `test -f 'nm-ip6-config.c' || echo '$(srcdir)/'`nm-ip6-config.c
+
+NetworkManager-nm-ip6-config.obj: nm-ip6-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-ip6-config.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-ip6-config.Tpo -c -o NetworkManager-nm-ip6-config.obj `if test -f 'nm-ip6-config.c'; then $(CYGPATH_W) 'nm-ip6-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-ip6-config.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-ip6-config.Tpo $(DEPDIR)/NetworkManager-nm-ip6-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-ip6-config.c' object='NetworkManager-nm-ip6-config.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-ip6-config.obj `if test -f 'nm-ip6-config.c'; then $(CYGPATH_W) 'nm-ip6-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-ip6-config.c'; fi`
+
+NetworkManager-nm-secrets-provider-interface.o: nm-secrets-provider-interface.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-secrets-provider-interface.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Tpo -c -o NetworkManager-nm-secrets-provider-interface.o `test -f 'nm-secrets-provider-interface.c' || echo '$(srcdir)/'`nm-secrets-provider-interface.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Tpo $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-secrets-provider-interface.c' object='NetworkManager-nm-secrets-provider-interface.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-secrets-provider-interface.o `test -f 'nm-secrets-provider-interface.c' || echo '$(srcdir)/'`nm-secrets-provider-interface.c
+
+NetworkManager-nm-secrets-provider-interface.obj: nm-secrets-provider-interface.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-secrets-provider-interface.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Tpo -c -o NetworkManager-nm-secrets-provider-interface.obj `if test -f 'nm-secrets-provider-interface.c'; then $(CYGPATH_W) 'nm-secrets-provider-interface.c'; else $(CYGPATH_W) '$(srcdir)/nm-secrets-provider-interface.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Tpo $(DEPDIR)/NetworkManager-nm-secrets-provider-interface.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-secrets-provider-interface.c' object='NetworkManager-nm-secrets-provider-interface.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-secrets-provider-interface.obj `if test -f 'nm-secrets-provider-interface.c'; then $(CYGPATH_W) 'nm-secrets-provider-interface.c'; else $(CYGPATH_W) '$(srcdir)/nm-secrets-provider-interface.c'; fi`
+
+NetworkManager-nm-active-connection.o: nm-active-connection.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-active-connection.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-active-connection.Tpo -c -o NetworkManager-nm-active-connection.o `test -f 'nm-active-connection.c' || echo '$(srcdir)/'`nm-active-connection.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-active-connection.Tpo $(DEPDIR)/NetworkManager-nm-active-connection.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-active-connection.c' object='NetworkManager-nm-active-connection.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-active-connection.o `test -f 'nm-active-connection.c' || echo '$(srcdir)/'`nm-active-connection.c
+
+NetworkManager-nm-active-connection.obj: nm-active-connection.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-active-connection.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-active-connection.Tpo -c -o NetworkManager-nm-active-connection.obj `if test -f 'nm-active-connection.c'; then $(CYGPATH_W) 'nm-active-connection.c'; else $(CYGPATH_W) '$(srcdir)/nm-active-connection.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-active-connection.Tpo $(DEPDIR)/NetworkManager-nm-active-connection.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-active-connection.c' object='NetworkManager-nm-active-connection.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-active-connection.obj `if test -f 'nm-active-connection.c'; then $(CYGPATH_W) 'nm-active-connection.c'; else $(CYGPATH_W) '$(srcdir)/nm-active-connection.c'; fi`
+
+NetworkManager-main.o: main.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-main.o -MD -MP -MF $(DEPDIR)/NetworkManager-main.Tpo -c -o NetworkManager-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-main.Tpo $(DEPDIR)/NetworkManager-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='main.c' object='NetworkManager-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+
+NetworkManager-main.obj: main.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-main.obj -MD -MP -MF $(DEPDIR)/NetworkManager-main.Tpo -c -o NetworkManager-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-main.Tpo $(DEPDIR)/NetworkManager-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='main.c' object='NetworkManager-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+
+NetworkManager-nm-policy.o: nm-policy.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy.Tpo -c -o NetworkManager-nm-policy.o `test -f 'nm-policy.c' || echo '$(srcdir)/'`nm-policy.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-policy.Tpo $(DEPDIR)/NetworkManager-nm-policy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-policy.c' object='NetworkManager-nm-policy.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy.o `test -f 'nm-policy.c' || echo '$(srcdir)/'`nm-policy.c
+
+NetworkManager-nm-policy.obj: nm-policy.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy.Tpo -c -o NetworkManager-nm-policy.obj `if test -f 'nm-policy.c'; then $(CYGPATH_W) 'nm-policy.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-policy.Tpo $(DEPDIR)/NetworkManager-nm-policy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-policy.c' object='NetworkManager-nm-policy.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy.obj `if test -f 'nm-policy.c'; then $(CYGPATH_W) 'nm-policy.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy.c'; fi`
+
+NetworkManager-nm-policy-hosts.o: nm-policy-hosts.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy-hosts.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy-hosts.Tpo -c -o NetworkManager-nm-policy-hosts.o `test -f 'nm-policy-hosts.c' || echo '$(srcdir)/'`nm-policy-hosts.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-policy-hosts.Tpo $(DEPDIR)/NetworkManager-nm-policy-hosts.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-policy-hosts.c' object='NetworkManager-nm-policy-hosts.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy-hosts.o `test -f 'nm-policy-hosts.c' || echo '$(srcdir)/'`nm-policy-hosts.c
+
+NetworkManager-nm-policy-hosts.obj: nm-policy-hosts.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy-hosts.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy-hosts.Tpo -c -o NetworkManager-nm-policy-hosts.obj `if test -f 'nm-policy-hosts.c'; then $(CYGPATH_W) 'nm-policy-hosts.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy-hosts.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-policy-hosts.Tpo $(DEPDIR)/NetworkManager-nm-policy-hosts.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-policy-hosts.c' object='NetworkManager-nm-policy-hosts.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy-hosts.obj `if test -f 'nm-policy-hosts.c'; then $(CYGPATH_W) 'nm-policy-hosts.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy-hosts.c'; fi`
+
+NetworkManager-nm-policy-hostname.o: nm-policy-hostname.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy-hostname.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy-hostname.Tpo -c -o NetworkManager-nm-policy-hostname.o `test -f 'nm-policy-hostname.c' || echo '$(srcdir)/'`nm-policy-hostname.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-policy-hostname.Tpo $(DEPDIR)/NetworkManager-nm-policy-hostname.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-policy-hostname.c' object='NetworkManager-nm-policy-hostname.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy-hostname.o `test -f 'nm-policy-hostname.c' || echo '$(srcdir)/'`nm-policy-hostname.c
+
+NetworkManager-nm-policy-hostname.obj: nm-policy-hostname.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-policy-hostname.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-policy-hostname.Tpo -c -o NetworkManager-nm-policy-hostname.obj `if test -f 'nm-policy-hostname.c'; then $(CYGPATH_W) 'nm-policy-hostname.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy-hostname.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-policy-hostname.Tpo $(DEPDIR)/NetworkManager-nm-policy-hostname.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-policy-hostname.c' object='NetworkManager-nm-policy-hostname.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-policy-hostname.obj `if test -f 'nm-policy-hostname.c'; then $(CYGPATH_W) 'nm-policy-hostname.c'; else $(CYGPATH_W) '$(srcdir)/nm-policy-hostname.c'; fi`
+
+NetworkManager-NetworkManagerUtils.o: NetworkManagerUtils.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-NetworkManagerUtils.o -MD -MP -MF $(DEPDIR)/NetworkManager-NetworkManagerUtils.Tpo -c -o NetworkManager-NetworkManagerUtils.o `test -f 'NetworkManagerUtils.c' || echo '$(srcdir)/'`NetworkManagerUtils.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-NetworkManagerUtils.Tpo $(DEPDIR)/NetworkManager-NetworkManagerUtils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerUtils.c' object='NetworkManager-NetworkManagerUtils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-NetworkManagerUtils.o `test -f 'NetworkManagerUtils.c' || echo '$(srcdir)/'`NetworkManagerUtils.c
+
+NetworkManager-NetworkManagerUtils.obj: NetworkManagerUtils.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-NetworkManagerUtils.obj -MD -MP -MF $(DEPDIR)/NetworkManager-NetworkManagerUtils.Tpo -c -o NetworkManager-NetworkManagerUtils.obj `if test -f 'NetworkManagerUtils.c'; then $(CYGPATH_W) 'NetworkManagerUtils.c'; else $(CYGPATH_W) '$(srcdir)/NetworkManagerUtils.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-NetworkManagerUtils.Tpo $(DEPDIR)/NetworkManager-NetworkManagerUtils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerUtils.c' object='NetworkManager-NetworkManagerUtils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-NetworkManagerUtils.obj `if test -f 'NetworkManagerUtils.c'; then $(CYGPATH_W) 'NetworkManagerUtils.c'; else $(CYGPATH_W) '$(srcdir)/NetworkManagerUtils.c'; fi`
+
+NetworkManager-nm-system.o: nm-system.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-system.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-system.Tpo -c -o NetworkManager-nm-system.o `test -f 'nm-system.c' || echo '$(srcdir)/'`nm-system.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-system.Tpo $(DEPDIR)/NetworkManager-nm-system.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-system.c' object='NetworkManager-nm-system.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-system.o `test -f 'nm-system.c' || echo '$(srcdir)/'`nm-system.c
+
+NetworkManager-nm-system.obj: nm-system.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-system.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-system.Tpo -c -o NetworkManager-nm-system.obj `if test -f 'nm-system.c'; then $(CYGPATH_W) 'nm-system.c'; else $(CYGPATH_W) '$(srcdir)/nm-system.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-system.Tpo $(DEPDIR)/NetworkManager-nm-system.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-system.c' object='NetworkManager-nm-system.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-system.obj `if test -f 'nm-system.c'; then $(CYGPATH_W) 'nm-system.c'; else $(CYGPATH_W) '$(srcdir)/nm-system.c'; fi`
+
+NetworkManager-nm-manager.o: nm-manager.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-manager.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-manager.Tpo -c -o NetworkManager-nm-manager.o `test -f 'nm-manager.c' || echo '$(srcdir)/'`nm-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-manager.Tpo $(DEPDIR)/NetworkManager-nm-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-manager.c' object='NetworkManager-nm-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-manager.o `test -f 'nm-manager.c' || echo '$(srcdir)/'`nm-manager.c
+
+NetworkManager-nm-manager.obj: nm-manager.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-manager.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-manager.Tpo -c -o NetworkManager-nm-manager.obj `if test -f 'nm-manager.c'; then $(CYGPATH_W) 'nm-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-manager.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-manager.Tpo $(DEPDIR)/NetworkManager-nm-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-manager.c' object='NetworkManager-nm-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-manager.obj `if test -f 'nm-manager.c'; then $(CYGPATH_W) 'nm-manager.c'; else $(CYGPATH_W) '$(srcdir)/nm-manager.c'; fi`
+
+NetworkManager-nm-netlink-monitor.o: nm-netlink-monitor.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-netlink-monitor.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-netlink-monitor.Tpo -c -o NetworkManager-nm-netlink-monitor.o `test -f 'nm-netlink-monitor.c' || echo '$(srcdir)/'`nm-netlink-monitor.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-netlink-monitor.Tpo $(DEPDIR)/NetworkManager-nm-netlink-monitor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-netlink-monitor.c' object='NetworkManager-nm-netlink-monitor.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-netlink-monitor.o `test -f 'nm-netlink-monitor.c' || echo '$(srcdir)/'`nm-netlink-monitor.c
+
+NetworkManager-nm-netlink-monitor.obj: nm-netlink-monitor.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-netlink-monitor.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-netlink-monitor.Tpo -c -o NetworkManager-nm-netlink-monitor.obj `if test -f 'nm-netlink-monitor.c'; then $(CYGPATH_W) 'nm-netlink-monitor.c'; else $(CYGPATH_W) '$(srcdir)/nm-netlink-monitor.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-netlink-monitor.Tpo $(DEPDIR)/NetworkManager-nm-netlink-monitor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-netlink-monitor.c' object='NetworkManager-nm-netlink-monitor.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-netlink-monitor.obj `if test -f 'nm-netlink-monitor.c'; then $(CYGPATH_W) 'nm-netlink-monitor.c'; else $(CYGPATH_W) '$(srcdir)/nm-netlink-monitor.c'; fi`
+
+NetworkManager-nm-activation-request.o: nm-activation-request.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-activation-request.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-activation-request.Tpo -c -o NetworkManager-nm-activation-request.o `test -f 'nm-activation-request.c' || echo '$(srcdir)/'`nm-activation-request.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-activation-request.Tpo $(DEPDIR)/NetworkManager-nm-activation-request.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-activation-request.c' object='NetworkManager-nm-activation-request.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-activation-request.o `test -f 'nm-activation-request.c' || echo '$(srcdir)/'`nm-activation-request.c
+
+NetworkManager-nm-activation-request.obj: nm-activation-request.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-activation-request.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-activation-request.Tpo -c -o NetworkManager-nm-activation-request.obj `if test -f 'nm-activation-request.c'; then $(CYGPATH_W) 'nm-activation-request.c'; else $(CYGPATH_W) '$(srcdir)/nm-activation-request.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-activation-request.Tpo $(DEPDIR)/NetworkManager-nm-activation-request.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-activation-request.c' object='NetworkManager-nm-activation-request.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-activation-request.obj `if test -f 'nm-activation-request.c'; then $(CYGPATH_W) 'nm-activation-request.c'; else $(CYGPATH_W) '$(srcdir)/nm-activation-request.c'; fi`
+
+NetworkManager-nm-properties-changed-signal.o: nm-properties-changed-signal.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-properties-changed-signal.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Tpo -c -o NetworkManager-nm-properties-changed-signal.o `test -f 'nm-properties-changed-signal.c' || echo '$(srcdir)/'`nm-properties-changed-signal.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Tpo $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-properties-changed-signal.c' object='NetworkManager-nm-properties-changed-signal.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-properties-changed-signal.o `test -f 'nm-properties-changed-signal.c' || echo '$(srcdir)/'`nm-properties-changed-signal.c
+
+NetworkManager-nm-properties-changed-signal.obj: nm-properties-changed-signal.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-properties-changed-signal.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Tpo -c -o NetworkManager-nm-properties-changed-signal.obj `if test -f 'nm-properties-changed-signal.c'; then $(CYGPATH_W) 'nm-properties-changed-signal.c'; else $(CYGPATH_W) '$(srcdir)/nm-properties-changed-signal.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Tpo $(DEPDIR)/NetworkManager-nm-properties-changed-signal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-properties-changed-signal.c' object='NetworkManager-nm-properties-changed-signal.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-properties-changed-signal.obj `if test -f 'nm-properties-changed-signal.c'; then $(CYGPATH_W) 'nm-properties-changed-signal.c'; else $(CYGPATH_W) '$(srcdir)/nm-properties-changed-signal.c'; fi`
+
+NetworkManager-wpa.o: wpa.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-wpa.o -MD -MP -MF $(DEPDIR)/NetworkManager-wpa.Tpo -c -o NetworkManager-wpa.o `test -f 'wpa.c' || echo '$(srcdir)/'`wpa.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-wpa.Tpo $(DEPDIR)/NetworkManager-wpa.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='wpa.c' object='NetworkManager-wpa.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-wpa.o `test -f 'wpa.c' || echo '$(srcdir)/'`wpa.c
+
+NetworkManager-wpa.obj: wpa.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-wpa.obj -MD -MP -MF $(DEPDIR)/NetworkManager-wpa.Tpo -c -o NetworkManager-wpa.obj `if test -f 'wpa.c'; then $(CYGPATH_W) 'wpa.c'; else $(CYGPATH_W) '$(srcdir)/wpa.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-wpa.Tpo $(DEPDIR)/NetworkManager-wpa.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='wpa.c' object='NetworkManager-wpa.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-wpa.obj `if test -f 'wpa.c'; then $(CYGPATH_W) 'wpa.c'; else $(CYGPATH_W) '$(srcdir)/wpa.c'; fi`
+
+NetworkManager-nm-dhcp4-config.o: nm-dhcp4-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dhcp4-config.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dhcp4-config.Tpo -c -o NetworkManager-nm-dhcp4-config.o `test -f 'nm-dhcp4-config.c' || echo '$(srcdir)/'`nm-dhcp4-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-dhcp4-config.Tpo $(DEPDIR)/NetworkManager-nm-dhcp4-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dhcp4-config.c' object='NetworkManager-nm-dhcp4-config.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dhcp4-config.o `test -f 'nm-dhcp4-config.c' || echo '$(srcdir)/'`nm-dhcp4-config.c
+
+NetworkManager-nm-dhcp4-config.obj: nm-dhcp4-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dhcp4-config.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dhcp4-config.Tpo -c -o NetworkManager-nm-dhcp4-config.obj `if test -f 'nm-dhcp4-config.c'; then $(CYGPATH_W) 'nm-dhcp4-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-dhcp4-config.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-dhcp4-config.Tpo $(DEPDIR)/NetworkManager-nm-dhcp4-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dhcp4-config.c' object='NetworkManager-nm-dhcp4-config.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dhcp4-config.obj `if test -f 'nm-dhcp4-config.c'; then $(CYGPATH_W) 'nm-dhcp4-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-dhcp4-config.c'; fi`
+
+NetworkManager-nm-dhcp6-config.o: nm-dhcp6-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dhcp6-config.o -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dhcp6-config.Tpo -c -o NetworkManager-nm-dhcp6-config.o `test -f 'nm-dhcp6-config.c' || echo '$(srcdir)/'`nm-dhcp6-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-dhcp6-config.Tpo $(DEPDIR)/NetworkManager-nm-dhcp6-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dhcp6-config.c' object='NetworkManager-nm-dhcp6-config.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dhcp6-config.o `test -f 'nm-dhcp6-config.c' || echo '$(srcdir)/'`nm-dhcp6-config.c
+
+NetworkManager-nm-dhcp6-config.obj: nm-dhcp6-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT NetworkManager-nm-dhcp6-config.obj -MD -MP -MF $(DEPDIR)/NetworkManager-nm-dhcp6-config.Tpo -c -o NetworkManager-nm-dhcp6-config.obj `if test -f 'nm-dhcp6-config.c'; then $(CYGPATH_W) 'nm-dhcp6-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-dhcp6-config.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/NetworkManager-nm-dhcp6-config.Tpo $(DEPDIR)/NetworkManager-nm-dhcp6-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dhcp6-config.c' object='NetworkManager-nm-dhcp6-config.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(NetworkManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o NetworkManager-nm-dhcp6-config.obj `if test -f 'nm-dhcp6-config.c'; then $(CYGPATH_W) 'nm-dhcp6-config.c'; else $(CYGPATH_W) '$(srcdir)/nm-dhcp6-config.c'; fi`
+
+nm_crash_logger-nm-crash-logger.o: nm-crash-logger.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_crash_logger_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm_crash_logger-nm-crash-logger.o -MD -MP -MF $(DEPDIR)/nm_crash_logger-nm-crash-logger.Tpo -c -o nm_crash_logger-nm-crash-logger.o `test -f 'nm-crash-logger.c' || echo '$(srcdir)/'`nm-crash-logger.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/nm_crash_logger-nm-crash-logger.Tpo $(DEPDIR)/nm_crash_logger-nm-crash-logger.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-crash-logger.c' object='nm_crash_logger-nm-crash-logger.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_crash_logger_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm_crash_logger-nm-crash-logger.o `test -f 'nm-crash-logger.c' || echo '$(srcdir)/'`nm-crash-logger.c
+
+nm_crash_logger-nm-crash-logger.obj: nm-crash-logger.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_crash_logger_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm_crash_logger-nm-crash-logger.obj -MD -MP -MF $(DEPDIR)/nm_crash_logger-nm-crash-logger.Tpo -c -o nm_crash_logger-nm-crash-logger.obj `if test -f 'nm-crash-logger.c'; then $(CYGPATH_W) 'nm-crash-logger.c'; else $(CYGPATH_W) '$(srcdir)/nm-crash-logger.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/nm_crash_logger-nm-crash-logger.Tpo $(DEPDIR)/nm_crash_logger-nm-crash-logger.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-crash-logger.c' object='nm_crash_logger-nm-crash-logger.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_crash_logger_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm_crash_logger-nm-crash-logger.obj `if test -f 'nm-crash-logger.c'; then $(CYGPATH_W) 'nm-crash-logger.c'; else $(CYGPATH_W) '$(srcdir)/nm-crash-logger.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+install-NetworkManagerDATA: $(NetworkManager_DATA)
+	@$(NORMAL_INSTALL)
+	test -z "$(NetworkManagerdir)" || $(MKDIR_P) "$(DESTDIR)$(NetworkManagerdir)"
+	@list='$(NetworkManager_DATA)'; test -n "$(NetworkManagerdir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(NetworkManagerdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(NetworkManagerdir)" || exit $$?; \
+	done
+
+uninstall-NetworkManagerDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(NetworkManager_DATA)'; test -n "$(NetworkManagerdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(NetworkManagerdir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(NetworkManagerdir)" && rm -f $$files
+install-dbusserviceDATA: $(dbusservice_DATA)
+	@$(NORMAL_INSTALL)
+	test -z "$(dbusservicedir)" || $(MKDIR_P) "$(DESTDIR)$(dbusservicedir)"
+	@list='$(dbusservice_DATA)'; test -n "$(dbusservicedir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbusservicedir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(dbusservicedir)" || exit $$?; \
+	done
+
+uninstall-dbusserviceDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(dbusservice_DATA)'; test -n "$(dbusservicedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(dbusservicedir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(dbusservicedir)" && rm -f $$files
+
+# 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.
+$(RECURSIVE_TARGETS):
+	@fail= failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	list='$(SUBDIRS)'; 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"
+
+$(RECURSIVE_CLEAN_TARGETS):
+	@fail= failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	rev=''; for subdir in $$list; do \
+	  if test "$$subdir" = "."; then :; else \
+	    rev="$$subdir $$rev"; \
+	  fi; \
+	done; \
+	rev="$$rev ."; \
+	target=`echo $@ | sed s/-recursive//`; \
+	for subdir in $$rev; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done && test -z "$$fail"
+tags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+	done
+ctags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+	done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	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; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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 \
+	    test -d "$(distdir)/$$subdir" \
+	    || $(MKDIR_P) "$(distdir)/$$subdir" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    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: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-recursive
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA)
+installdirs: installdirs-recursive
+installdirs-am:
+	for dir in "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(NetworkManagerdir)" "$(DESTDIR)$(dbusservicedir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) 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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+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."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+	clean-noinstLTLIBRARIES clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-NetworkManagerDATA install-dbusserviceDATA
+	@$(NORMAL_INSTALL)
+	$(MAKE) $(AM_MAKEFLAGS) install-data-hook
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS install-sbinPROGRAMS
+
+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 -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-NetworkManagerDATA uninstall-dbusserviceDATA \
+	uninstall-libexecPROGRAMS uninstall-sbinPROGRAMS
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all check \
+	ctags-recursive install install-am install-data-am \
+	install-strip tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+	all all-am check check-am clean clean-generic \
+	clean-libexecPROGRAMS clean-libtool clean-noinstLTLIBRARIES \
+	clean-sbinPROGRAMS ctags ctags-recursive distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-NetworkManagerDATA install-am install-data \
+	install-data-am install-data-hook install-dbusserviceDATA \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-info install-info-am \
+	install-libexecPROGRAMS install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-sbinPROGRAMS install-strip \
+	installcheck installcheck-am installdirs installdirs-am \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am tags tags-recursive uninstall \
+	uninstall-NetworkManagerDATA uninstall-am \
+	uninstall-dbusserviceDATA uninstall-libexecPROGRAMS \
+	uninstall-sbinPROGRAMS
+
+
+nm-access-point-glue.h: $(top_srcdir)/introspection/nm-access-point.xml
+	dbus-binding-tool --prefix=nm_access_point --mode=glib-server --output=$@ $<
+
+nm-manager-glue.h: $(top_srcdir)/introspection/nm-manager.xml
+	dbus-binding-tool --prefix=nm_manager --mode=glib-server --output=$@ $<
+
+nm-device-interface-glue.h: $(top_srcdir)/introspection/nm-device.xml
+	dbus-binding-tool --prefix=nm_device_interface --mode=glib-server --output=$@ $<
+
+nm-device-ethernet-glue.h: $(top_srcdir)/introspection/nm-device-ethernet.xml
+	dbus-binding-tool --prefix=nm_device_ethernet --mode=glib-server --output=$@ $<
+
+nm-device-wifi-glue.h: $(top_srcdir)/introspection/nm-device-wifi.xml
+	dbus-binding-tool --prefix=nm_device_wifi --mode=glib-server --output=$@ $<
+
+nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml
+	dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $<
+
+nm-device-olpc-mesh-glue.h: $(top_srcdir)/introspection/nm-device-olpc-mesh.xml
+	dbus-binding-tool --prefix=nm_device_olpc_mesh --mode=glib-server --output=$@ $<
+
+nm-ip4-config-glue.h: $(top_srcdir)/introspection/nm-ip4-config.xml
+	dbus-binding-tool --prefix=nm_ip4_config --mode=glib-server --output=$@ $<
+
+nm-ip6-config-glue.h: $(top_srcdir)/introspection/nm-ip6-config.xml
+	dbus-binding-tool --prefix=nm_ip6_config --mode=glib-server --output=$@ $<
+
+nm-active-connection-glue.h: $(top_srcdir)/introspection/nm-active-connection.xml
+	dbus-binding-tool --prefix=nm_active_connection --mode=glib-server --output=$@ $<
+
+nm-dhcp4-config-glue.h: $(top_srcdir)/introspection/nm-dhcp4-config.xml
+	dbus-binding-tool --prefix=nm_dhcp4_config --mode=glib-server --output=$@ $<
+
+nm-dhcp6-config-glue.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml
+	dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-server --output=$@ $<
+
+nm-device-cdma-glue.h: $(top_srcdir)/introspection/nm-device-cdma.xml
+	dbus-binding-tool --prefix=nm_device_cdma --mode=glib-server --output=$@ $<
+
+nm-device-gsm-glue.h: $(top_srcdir)/introspection/nm-device-gsm.xml
+	dbus-binding-tool --prefix=nm_device_gsm --mode=glib-server --output=$@ $<
+install-data-hook:
+	$(mkinstalldirs) -m 0700 $(DESTDIR)$(rundir)
+
+# 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/NetworkManager.conf b/src/NetworkManager.conf
new file mode 100644
index 00000000..8d083140
--- /dev/null
+++ b/src/NetworkManager.conf
@@ -0,0 +1,82 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+        <policy user="root">
+                <allow own="org.freedesktop.NetworkManager"/>
+                <allow own="org.freedesktop.NetworkManagerSystemSettings"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"/>
+                <allow send_destination="org.freedesktop.NetworkManagerSystemSettings"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.PPP"/>
+        </policy>
+        <policy at_console="true">
+                <allow send_destination="org.freedesktop.NetworkManager"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.DBus.Introspectable"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.DBus.Properties"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.AccessPoint"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.Connection.Active"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.Device.Cdma"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.Device.Wired"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.Device.Gsm"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.Device.Serial"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.Device.Wireless"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.Device"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.DHCP4Config"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.IP4Config"/>
+
+                <allow send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager.VPN.Connection"/>
+
+                <deny send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager"
+                       send_member="SetLogging"/>
+        </policy>
+        <policy context="default">
+                <deny own="org.freedesktop.NetworkManager"/>
+                <deny own="org.freedesktop.NetworkManagerSystemSettings"/>
+
+                <deny send_destination="org.freedesktop.NetworkManager"/>
+                <allow send_destination="org.freedesktop.NetworkManagerSystemSettings"/>
+
+                <deny send_destination="org.freedesktop.NetworkManager"
+                       send_interface="org.freedesktop.NetworkManager"
+                       send_member="SetLogging"/>
+
+                <!-- The org.freedesktop.NetworkManagerSettings.Connection.Secrets
+                     interface is secured via PolicyKit.
+                  -->
+        </policy>
+
+        <limit name="max_replies_per_connection">512</limit>
+</busconfig>
+
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
new file mode 100644
index 00000000..22cf2fa0
--- /dev/null
+++ b/src/NetworkManagerUtils.c
@@ -0,0 +1,625 @@
+/* -*- 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) 2004 - 2010 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#include <glib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "NetworkManagerUtils.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "nm-device.h"
+#include "nm-device-wifi.h"
+#include "nm-device-ethernet.h"
+#include "nm-dbus-manager.h"
+#include "nm-dispatcher-action.h"
+#include "nm-dbus-glib-types.h"
+
+#include <netlink/addr.h>
+#include <netinet/in.h>
+
+/*
+ * nm_ethernet_address_is_valid
+ *
+ * Compares an Ethernet address against known invalid addresses.
+ *
+ */
+gboolean
+nm_ethernet_address_is_valid (const struct ether_addr *test_addr)
+{
+	guint8 invalid_addr1[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+	guint8 invalid_addr2[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	guint8 invalid_addr3[ETH_ALEN] = {0x44, 0x44, 0x44, 0x44, 0x44, 0x44};
+	guint8 invalid_addr4[ETH_ALEN] = {0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}; /* prism54 dummy MAC */
+
+	g_return_val_if_fail (test_addr != NULL, FALSE);
+
+	/* Compare the AP address the card has with invalid ethernet MAC addresses. */
+	if (!memcmp (test_addr->ether_addr_octet, &invalid_addr1, ETH_ALEN))
+		return FALSE;
+
+	if (!memcmp (test_addr->ether_addr_octet, &invalid_addr2, ETH_ALEN))
+		return FALSE;
+
+	if (!memcmp (test_addr->ether_addr_octet, &invalid_addr3, ETH_ALEN))
+		return FALSE;
+
+	if (!memcmp (test_addr->ether_addr_octet, &invalid_addr4, ETH_ALEN))
+		return FALSE;
+
+	if (test_addr->ether_addr_octet[0] & 1)			/* Multicast addresses */
+		return FALSE;
+	
+	return TRUE;
+}
+
+
+int
+nm_spawn_process (const char *args)
+{
+	gint num_args;
+	char **argv = NULL;
+	int status = -1;
+	GError *error = NULL;
+
+	g_return_val_if_fail (args != NULL, -1);
+
+	if (!g_shell_parse_argv (args, &num_args, &argv, &error)) {
+		nm_log_warn (LOGD_CORE, "could not parse arguments for '%s': %s", args, error->message);
+		g_error_free (error);
+		return -1;
+	}
+
+	if (!g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &error)) {
+		nm_log_warn (LOGD_CORE, "could not spawn process '%s': %s", args, error->message);
+		g_error_free (error);
+	}
+
+	g_strfreev (argv);
+	return status;
+}
+
+/*
+ * nm_utils_ip4_netmask_to_prefix
+ *
+ * Figure out the network prefix from a netmask.  Netmask
+ * MUST be in network byte order.
+ *
+ */
+guint32
+nm_utils_ip4_netmask_to_prefix (guint32 netmask)
+{
+	guchar *p, *end;
+	guint32 prefix = 0;
+
+	p = (guchar *) &netmask;
+	end = p + sizeof (guint32);
+
+	while ((*p == 0xFF) && p < end) {
+		prefix += 8;
+		p++;
+	}
+
+	if (p < end) {
+		guchar v = *p;
+
+		while (v) {
+			prefix++;
+			v <<= 1;
+		}
+	}
+
+	return prefix;
+}
+
+/*
+ * nm_utils_ip4_prefix_to_netmask
+ *
+ * Figure out the netmask from a prefix.
+ *
+ */
+guint32
+nm_utils_ip4_prefix_to_netmask (guint32 prefix)
+{
+	guint32 msk = 0x80000000;
+	guint32 netmask = 0;
+
+	while (prefix > 0) {
+		netmask |= msk;
+		msk >>= 1;
+		prefix--;
+	}
+
+	return (guint32) htonl (netmask);
+}
+
+char *
+nm_ether_ntop (const struct ether_addr *mac)
+{
+	/* we like leading zeros and all-caps, instead
+	 * of what glibc's ether_ntop() gives us
+	 */
+	return g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+	                        mac->ether_addr_octet[0], mac->ether_addr_octet[1],
+	                        mac->ether_addr_octet[2], mac->ether_addr_octet[3],
+	                        mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
+}
+
+void
+nm_utils_merge_ip4_config (NMIP4Config *ip4_config, NMSettingIP4Config *setting)
+{
+	int i, j;
+
+	if (!setting)
+		return; /* Defaults are just fine */
+
+	if (nm_setting_ip4_config_get_ignore_auto_dns (setting)) {
+		nm_ip4_config_reset_nameservers (ip4_config);
+		nm_ip4_config_reset_domains (ip4_config);
+		nm_ip4_config_reset_searches (ip4_config);
+	}
+
+	if (nm_setting_ip4_config_get_ignore_auto_routes (setting))
+		nm_ip4_config_reset_routes (ip4_config);
+
+	for (i = 0; i < nm_setting_ip4_config_get_num_dns (setting); i++) {
+		guint32 ns;
+		gboolean found = FALSE;
+
+		/* Avoid dupes */
+		ns = nm_setting_ip4_config_get_dns (setting, i);
+		for (j = 0; j < nm_ip4_config_get_num_nameservers (ip4_config); j++) {
+			if (nm_ip4_config_get_nameserver (ip4_config, j) == ns) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			nm_ip4_config_add_nameserver (ip4_config, ns);
+	}
+
+	/* DNS search domains */
+	for (i = 0; i < nm_setting_ip4_config_get_num_dns_searches (setting); i++) {
+		const char *search = nm_setting_ip4_config_get_dns_search (setting, i);
+		gboolean found = FALSE;
+
+		/* Avoid dupes */
+		for (j = 0; j < nm_ip4_config_get_num_searches (ip4_config); j++) {
+			if (!strcmp (search, nm_ip4_config_get_search (ip4_config, j))) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			nm_ip4_config_add_search (ip4_config, search);
+	}
+
+	/* IPv4 addresses */
+	for (i = 0; i < nm_setting_ip4_config_get_num_addresses (setting); i++) {
+		NMIP4Address *setting_addr = nm_setting_ip4_config_get_address (setting, i);
+		guint32 num;
+
+		num = nm_ip4_config_get_num_addresses (ip4_config);
+		for (j = 0; j < num; j++) {
+			NMIP4Address *cfg_addr = nm_ip4_config_get_address (ip4_config, j);
+
+			/* Dupe, override with user-specified address */
+			if (nm_ip4_address_get_address (cfg_addr) == nm_ip4_address_get_address (setting_addr)) {
+				nm_ip4_config_replace_address (ip4_config, j, setting_addr);
+				break;
+			}
+		}
+
+		if (j == num)
+			nm_ip4_config_add_address (ip4_config, setting_addr);
+	}
+
+	/* IPv4 routes */
+	for (i = 0; i < nm_setting_ip4_config_get_num_routes (setting); i++) {
+		NMIP4Route *setting_route = nm_setting_ip4_config_get_route (setting, i);
+		guint32 num;
+
+		num = nm_ip4_config_get_num_routes (ip4_config);
+		for (j = 0; j < num; j++) {
+			NMIP4Route *cfg_route = nm_ip4_config_get_route (ip4_config, j);
+
+			/* Dupe, override with user-specified route */
+			if (   (nm_ip4_route_get_dest (cfg_route) == nm_ip4_route_get_dest (setting_route))
+			    && (nm_ip4_route_get_prefix (cfg_route) == nm_ip4_route_get_prefix (setting_route))
+			    && (nm_ip4_route_get_next_hop (cfg_route) == nm_ip4_route_get_next_hop (setting_route))) {
+				nm_ip4_config_replace_route (ip4_config, j, setting_route);
+				break;
+			}
+		}
+
+		if (j == num)
+			nm_ip4_config_add_route (ip4_config, setting_route);
+	}
+
+	if (nm_setting_ip4_config_get_never_default (setting))
+		nm_ip4_config_set_never_default (ip4_config, TRUE);
+}
+
+static inline gboolean
+ip6_addresses_equal (const struct in6_addr *a, const struct in6_addr *b)
+{
+	return memcmp (a, b, sizeof (struct in6_addr)) == 0;
+}
+
+/* This is exactly identical to nm_utils_merge_ip4_config, with s/4/6/,
+ * except that we can't compare addresses with ==.
+ */
+void
+nm_utils_merge_ip6_config (NMIP6Config *ip6_config, NMSettingIP6Config *setting)
+{
+	int i, j;
+
+	if (!setting)
+		return; /* Defaults are just fine */
+
+	if (nm_setting_ip6_config_get_ignore_auto_dns (setting)) {
+		nm_ip6_config_reset_nameservers (ip6_config);
+		nm_ip6_config_reset_domains (ip6_config);
+		nm_ip6_config_reset_searches (ip6_config);
+	}
+
+	if (nm_setting_ip6_config_get_ignore_auto_routes (setting))
+		nm_ip6_config_reset_routes (ip6_config);
+
+	for (i = 0; i < nm_setting_ip6_config_get_num_dns (setting); i++) {
+		const struct in6_addr *ns;
+		gboolean found = FALSE;
+
+		/* Avoid dupes */
+		ns = nm_setting_ip6_config_get_dns (setting, i);
+		for (j = 0; j < nm_ip6_config_get_num_nameservers (ip6_config); j++) {
+			if (ip6_addresses_equal (nm_ip6_config_get_nameserver (ip6_config, j), ns)) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			nm_ip6_config_add_nameserver (ip6_config, ns);
+	}
+
+	/* DNS search domains */
+	for (i = 0; i < nm_setting_ip6_config_get_num_dns_searches (setting); i++) {
+		const char *search = nm_setting_ip6_config_get_dns_search (setting, i);
+		gboolean found = FALSE;
+
+		/* Avoid dupes */
+		for (j = 0; j < nm_ip6_config_get_num_searches (ip6_config); j++) {
+			if (!strcmp (search, nm_ip6_config_get_search (ip6_config, j))) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			nm_ip6_config_add_search (ip6_config, search);
+	}
+
+	/* IPv6 addresses */
+	for (i = 0; i < nm_setting_ip6_config_get_num_addresses (setting); i++) {
+		NMIP6Address *setting_addr = nm_setting_ip6_config_get_address (setting, i);
+		guint32 num;
+
+		num = nm_ip6_config_get_num_addresses (ip6_config);
+		for (j = 0; j < num; j++) {
+			NMIP6Address *cfg_addr = nm_ip6_config_get_address (ip6_config, j);
+
+			/* Dupe, override with user-specified address */
+			if (ip6_addresses_equal (nm_ip6_address_get_address (cfg_addr), nm_ip6_address_get_address (setting_addr))) {
+				nm_ip6_config_replace_address (ip6_config, j, setting_addr);
+				break;
+			}
+		}
+
+		if (j == num)
+			nm_ip6_config_add_address (ip6_config, setting_addr);
+	}
+
+	/* IPv6 routes */
+	for (i = 0; i < nm_setting_ip6_config_get_num_routes (setting); i++) {
+		NMIP6Route *setting_route = nm_setting_ip6_config_get_route (setting, i);
+		guint32 num;
+
+		num = nm_ip6_config_get_num_routes (ip6_config);
+		for (j = 0; j < num; j++) {
+			NMIP6Route *cfg_route = nm_ip6_config_get_route (ip6_config, j);
+
+			/* Dupe, override with user-specified route */
+			if (   ip6_addresses_equal (nm_ip6_route_get_dest (cfg_route), nm_ip6_route_get_dest (setting_route))
+			    && (nm_ip6_route_get_prefix (cfg_route) == nm_ip6_route_get_prefix (setting_route))
+				&& ip6_addresses_equal (nm_ip6_route_get_next_hop (cfg_route), nm_ip6_route_get_next_hop (setting_route))) {
+				nm_ip6_config_replace_route (ip6_config, j, setting_route);
+				break;
+			}
+		}
+
+		if (j == num)
+			nm_ip6_config_add_route (ip6_config, setting_route);
+	}
+
+	if (nm_setting_ip6_config_get_never_default (setting))
+		nm_ip6_config_set_never_default (ip6_config, TRUE);
+}
+
+void
+nm_utils_call_dispatcher (const char *action,
+                          NMConnection *connection,
+                          NMDevice *device,
+                          const char *vpn_iface)
+{
+	NMDBusManager *dbus_mgr;
+	DBusGProxy *proxy;
+	DBusGConnection *g_connection;
+	GHashTable *connection_hash;
+	GHashTable *connection_props;
+	GHashTable *device_props;
+
+	g_return_if_fail (action != NULL);
+
+	/* All actions except 'hostname' require a device */
+	if (strcmp (action, "hostname"))
+		g_return_if_fail (NM_IS_DEVICE (device));
+
+	dbus_mgr = nm_dbus_manager_get ();
+	g_connection = nm_dbus_manager_get_connection (dbus_mgr);
+	proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                   NM_DISPATCHER_DBUS_SERVICE,
+	                                   NM_DISPATCHER_DBUS_PATH,
+	                                   NM_DISPATCHER_DBUS_IFACE);
+	if (!proxy) {
+		nm_log_err (LOGD_CORE, "could not get dispatcher proxy!");
+		g_object_unref (dbus_mgr);
+		return;
+	}
+
+	if (connection) {
+		connection_hash = nm_connection_to_hash (connection);
+
+		connection_props = value_hash_create ();
+
+		/* Service name */
+		if (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_USER) {
+			value_hash_add_str (connection_props,
+								NMD_CONNECTION_PROPS_SERVICE_NAME,
+								NM_DBUS_SERVICE_USER_SETTINGS);
+		} else if (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_SYSTEM) {
+			value_hash_add_str (connection_props,
+								NMD_CONNECTION_PROPS_SERVICE_NAME,
+								NM_DBUS_SERVICE_SYSTEM_SETTINGS);
+		}
+
+		/* path */
+		value_hash_add_object_path (connection_props,
+									NMD_CONNECTION_PROPS_PATH,
+									nm_connection_get_path (connection));
+	} else {
+		connection_hash = value_hash_create ();
+		connection_props = value_hash_create ();
+	}
+
+	device_props = value_hash_create ();
+
+	/* Hostname actions do not require a device */
+	if (strcmp (action, "hostname")) {
+		/* interface */
+		value_hash_add_str (device_props, NMD_DEVICE_PROPS_INTERFACE, nm_device_get_iface (device));
+
+		/* IP interface */
+		if (vpn_iface) {
+			value_hash_add_str (device_props, NMD_DEVICE_PROPS_IP_INTERFACE, vpn_iface);
+		} else if (nm_device_get_ip_iface (device)) {
+			value_hash_add_str (device_props, NMD_DEVICE_PROPS_IP_INTERFACE, nm_device_get_ip_iface (device));
+		}
+
+		/* type */
+		value_hash_add_uint (device_props, NMD_DEVICE_PROPS_TYPE, nm_device_get_device_type (device));
+
+		/* state */
+		value_hash_add_uint (device_props, NMD_DEVICE_PROPS_STATE, nm_device_get_state (device));
+		value_hash_add_object_path (device_props, NMD_DEVICE_PROPS_PATH, nm_device_get_path (device));
+	}
+
+	dbus_g_proxy_call_no_reply (proxy, "Action",
+	                            G_TYPE_STRING, action,
+	                            DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
+	                            DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
+	                            DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
+	                            G_TYPE_INVALID);
+
+	g_object_unref (proxy);
+	g_hash_table_destroy (connection_hash);
+	g_hash_table_destroy (connection_props);
+	g_hash_table_destroy (device_props);
+	g_object_unref (dbus_mgr);
+}
+
+gboolean
+nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
+{
+	const GSList *iter;
+	char *hwaddr_match, *p;
+
+	g_return_val_if_fail (hwaddr != NULL, FALSE);
+
+	p = hwaddr_match = g_strdup_printf ("mac:%s", hwaddr);
+
+	while (*p) {
+		*p = g_ascii_tolower (*p);
+		p++;
+	}
+
+	for (iter = specs; iter; iter = g_slist_next (iter)) {
+		if (!strcmp ((const char *) iter->data, hwaddr_match)) {
+			g_free (hwaddr_match);
+			return TRUE;
+		}
+	}
+
+	g_free (hwaddr_match);
+	return FALSE;
+}
+
+/*********************************/
+
+static void
+nm_gvalue_destroy (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+GHashTable *
+value_hash_create (void)
+{
+	return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy);
+}
+
+void
+value_hash_add (GHashTable *hash,
+				const char *key,
+				GValue *value)
+{
+	g_hash_table_insert (hash, g_strdup (key), value);
+}
+
+void
+value_hash_add_str (GHashTable *hash,
+					const char *key,
+					const char *str)
+{
+	GValue *value;
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, G_TYPE_STRING);
+	g_value_set_string (value, str);
+
+	value_hash_add (hash, key, value);
+}
+
+void
+value_hash_add_object_path (GHashTable *hash,
+							const char *key,
+							const char *op)
+{
+	GValue *value;
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, DBUS_TYPE_G_OBJECT_PATH);
+	g_value_set_boxed (value, op);
+
+	value_hash_add (hash, key, value);
+}
+
+void
+value_hash_add_uint (GHashTable *hash,
+					 const char *key,
+					 guint32 val)
+{
+	GValue *value;
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, G_TYPE_UINT);
+	g_value_set_uint (value, val);
+
+	value_hash_add (hash, key, value);
+}
+
+void
+value_hash_add_bool (GHashTable *hash,
+					 const char *key,
+					 gboolean val)
+{
+	GValue *value;
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, G_TYPE_BOOLEAN);
+	g_value_set_boolean (value, val);
+
+	value_hash_add (hash, key, value);
+}
+
+gboolean
+nm_utils_do_sysctl (const char *path, const char *value)
+{
+	int fd, len, nwrote, total;
+
+	fd = open (path, O_WRONLY | O_TRUNC);
+	if (fd == -1)
+		return FALSE;
+
+	len = strlen (value);
+	total = 0;
+	do {
+		nwrote = write (fd, value + total, len - total);
+		if (nwrote == -1) {
+			if (errno == EINTR)
+				continue;
+			close (fd);
+			return FALSE;
+		}
+		total += nwrote;
+	} while (total < len);
+
+	close (fd);
+	return TRUE;
+}
+
+gboolean
+nm_utils_get_proc_sys_net_value (const char *path,
+                                 const char *iface,
+                                 guint32 *out_value)
+{
+	GError *error = NULL;
+	char *contents = NULL;
+	gboolean success = FALSE;
+	long int tmp;
+
+	if (!g_file_get_contents (path, &contents, NULL, &error)) {
+		nm_log_dbg (LOGD_DEVICE, "(%s): error reading %s: (%d) %s",
+		            iface, path,
+		            error ? error->code : -1,
+		            error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+	} else {
+		errno = 0;
+		tmp = strtol (contents, NULL, 10);
+		if ((errno == 0) && (tmp == 0 || tmp == 1)) {
+			*out_value = (guint32) tmp;
+			success = TRUE;
+		}
+		g_free (contents);
+	}
+
+	return success;
+}
+
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
new file mode 100644
index 00000000..e3d1793b
--- /dev/null
+++ b/src/NetworkManagerUtils.h
@@ -0,0 +1,80 @@
+/* -*- 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) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#ifndef NETWORK_MANAGER_UTILS_H
+#define NETWORK_MANAGER_UTILS_H
+
+#include <glib.h>
+#include <stdio.h>
+#include <net/ethernet.h>
+
+#include "nm-device.h"
+#include "nm-ip4-config.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-ip6-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-connection.h"
+
+gboolean nm_ethernet_address_is_valid (const struct ether_addr *test_addr);
+
+int nm_spawn_process (const char *args);
+
+char *nm_ether_ntop (const struct ether_addr *mac);
+
+void nm_utils_merge_ip4_config (NMIP4Config *ip4_config, NMSettingIP4Config *setting);
+void nm_utils_merge_ip6_config (NMIP6Config *ip6_config, NMSettingIP6Config *setting);
+
+void nm_utils_call_dispatcher (const char *action,
+                               NMConnection *connection,
+                               NMDevice *device,
+                               const char *vpn_iface);
+
+gboolean nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr);
+
+
+GHashTable *value_hash_create          (void);
+void        value_hash_add             (GHashTable *hash,
+										const char *key,
+										GValue *value);
+
+void        value_hash_add_str         (GHashTable *hash,
+										const char *key,
+										const char *str);
+
+void        value_hash_add_object_path (GHashTable *hash,
+										const char *key,
+										const char *op);
+
+void        value_hash_add_uint        (GHashTable *hash,
+										const char *key,
+										guint32 val);
+
+void        value_hash_add_bool        (GHashTable *hash,
+					                    const char *key,
+					                    gboolean val);
+
+gboolean nm_utils_do_sysctl (const char *path, const char *value);
+
+gboolean nm_utils_get_proc_sys_net_value (const char *path,
+                                          const char *iface,
+                                          guint32 *out_value);
+
+#endif /* NETWORK_MANAGER_UTILS_H */
diff --git a/src/backends/Makefile.am b/src/backends/Makefile.am
new file mode 100644
index 00000000..c3485e5f
--- /dev/null
+++ b/src/backends/Makefile.am
@@ -0,0 +1,70 @@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/vpn-manager \
+	-I${top_srcdir}/src/named-manager \
+	-I${top_srcdir}/libnm-util
+
+noinst_LTLIBRARIES = libnmbackend.la
+
+libnmbackend_la_SOURCES = NetworkManagerGeneric.c	\
+				NetworkManagerGeneric.h
+
+libnmbackend_la_LIBADD =
+
+if TARGET_REDHAT
+libnmbackend_la_SOURCES += NetworkManagerRedHat.c
+endif
+
+if TARGET_SUSE
+libnmbackend_la_SOURCES += NetworkManagerSuSE.c
+endif
+
+if TARGET_GENTOO
+libnmbackend_la_SOURCES += NetworkManagerGentoo.c
+endif
+
+if TARGET_DEBIAN
+libnmbackend_la_SOURCES += NetworkManagerDebian.c
+endif
+
+if TARGET_SLACKWARE
+libnmbackend_la_SOURCES += NetworkManagerSlackware.c
+endif
+
+if TARGET_ARCH
+libnmbackend_la_SOURCES += NetworkManagerArch.c
+endif
+
+if TARGET_PALDO
+libnmbackend_la_SOURCES += NetworkManagerPaldo.c
+endif
+
+if TARGET_FRUGALWARE
+libnmbackend_la_SOURCES += NetworkManagerFrugalware.c
+libnmbackend_la_LIBADD += -lfwnetconfig -lfwutil
+endif
+
+if TARGET_MANDRIVA
+libnmbackend_la_SOURCES += NetworkManagerMandriva.c
+endif
+
+if TARGET_PARDUS
+libnmbackend_la_SOURCES += NetworkManagerPardus.c
+endif
+
+libnmbackend_la_LIBADD += \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+libnmbackend_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\"
+
diff --git a/src/backends/Makefile.in b/src/backends/Makefile.in
new file mode 100644
index 00000000..9e3b8735
--- /dev/null
+++ b/src/backends/Makefile.in
@@ -0,0 +1,689 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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@
+@TARGET_REDHAT_TRUE@am__append_1 = NetworkManagerRedHat.c
+@TARGET_SUSE_TRUE@am__append_2 = NetworkManagerSuSE.c
+@TARGET_GENTOO_TRUE@am__append_3 = NetworkManagerGentoo.c
+@TARGET_DEBIAN_TRUE@am__append_4 = NetworkManagerDebian.c
+@TARGET_SLACKWARE_TRUE@am__append_5 = NetworkManagerSlackware.c
+@TARGET_ARCH_TRUE@am__append_6 = NetworkManagerArch.c
+@TARGET_PALDO_TRUE@am__append_7 = NetworkManagerPaldo.c
+@TARGET_FRUGALWARE_TRUE@am__append_8 = NetworkManagerFrugalware.c
+@TARGET_FRUGALWARE_TRUE@am__append_9 = -lfwnetconfig -lfwutil
+@TARGET_MANDRIVA_TRUE@am__append_10 = NetworkManagerMandriva.c
+@TARGET_PARDUS_TRUE@am__append_11 = NetworkManagerPardus.c
+subdir = src/backends
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libnmbackend_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__libnmbackend_la_SOURCES_DIST = NetworkManagerGeneric.c \
+	NetworkManagerGeneric.h NetworkManagerRedHat.c \
+	NetworkManagerSuSE.c NetworkManagerGentoo.c \
+	NetworkManagerDebian.c NetworkManagerSlackware.c \
+	NetworkManagerArch.c NetworkManagerPaldo.c \
+	NetworkManagerFrugalware.c NetworkManagerMandriva.c \
+	NetworkManagerPardus.c
+@TARGET_REDHAT_TRUE@am__objects_1 =  \
+@TARGET_REDHAT_TRUE@	libnmbackend_la-NetworkManagerRedHat.lo
+@TARGET_SUSE_TRUE@am__objects_2 =  \
+@TARGET_SUSE_TRUE@	libnmbackend_la-NetworkManagerSuSE.lo
+@TARGET_GENTOO_TRUE@am__objects_3 =  \
+@TARGET_GENTOO_TRUE@	libnmbackend_la-NetworkManagerGentoo.lo
+@TARGET_DEBIAN_TRUE@am__objects_4 =  \
+@TARGET_DEBIAN_TRUE@	libnmbackend_la-NetworkManagerDebian.lo
+@TARGET_SLACKWARE_TRUE@am__objects_5 = libnmbackend_la-NetworkManagerSlackware.lo
+@TARGET_ARCH_TRUE@am__objects_6 =  \
+@TARGET_ARCH_TRUE@	libnmbackend_la-NetworkManagerArch.lo
+@TARGET_PALDO_TRUE@am__objects_7 =  \
+@TARGET_PALDO_TRUE@	libnmbackend_la-NetworkManagerPaldo.lo
+@TARGET_FRUGALWARE_TRUE@am__objects_8 = libnmbackend_la-NetworkManagerFrugalware.lo
+@TARGET_MANDRIVA_TRUE@am__objects_9 = libnmbackend_la-NetworkManagerMandriva.lo
+@TARGET_PARDUS_TRUE@am__objects_10 =  \
+@TARGET_PARDUS_TRUE@	libnmbackend_la-NetworkManagerPardus.lo
+am_libnmbackend_la_OBJECTS = libnmbackend_la-NetworkManagerGeneric.lo \
+	$(am__objects_1) $(am__objects_2) $(am__objects_3) \
+	$(am__objects_4) $(am__objects_5) $(am__objects_6) \
+	$(am__objects_7) $(am__objects_8) $(am__objects_9) \
+	$(am__objects_10)
+libnmbackend_la_OBJECTS = $(am_libnmbackend_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libnmbackend_la_SOURCES)
+DIST_SOURCES = $(am__libnmbackend_la_SOURCES_DIST)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/vpn-manager \
+	-I${top_srcdir}/src/named-manager \
+	-I${top_srcdir}/libnm-util
+
+noinst_LTLIBRARIES = libnmbackend.la
+libnmbackend_la_SOURCES = NetworkManagerGeneric.c \
+	NetworkManagerGeneric.h $(am__append_1) $(am__append_2) \
+	$(am__append_3) $(am__append_4) $(am__append_5) \
+	$(am__append_6) $(am__append_7) $(am__append_8) \
+	$(am__append_10) $(am__append_11)
+libnmbackend_la_LIBADD = $(am__append_9) \
+	$(top_builddir)/src/logging/libnm-logging.la $(DBUS_LIBS) \
+	$(GLIB_LIBS)
+libnmbackend_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\"
+
+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/backends/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/backends/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libnmbackend.la: $(libnmbackend_la_OBJECTS) $(libnmbackend_la_DEPENDENCIES) 
+	$(LINK)  $(libnmbackend_la_OBJECTS) $(libnmbackend_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerArch.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerDebian.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerFrugalware.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerGeneric.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerGentoo.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerMandriva.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerPaldo.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerPardus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerRedHat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerSlackware.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnmbackend_la-NetworkManagerSuSE.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libnmbackend_la-NetworkManagerGeneric.lo: NetworkManagerGeneric.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerGeneric.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerGeneric.Tpo -c -o libnmbackend_la-NetworkManagerGeneric.lo `test -f 'NetworkManagerGeneric.c' || echo '$(srcdir)/'`NetworkManagerGeneric.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerGeneric.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerGeneric.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerGeneric.c' object='libnmbackend_la-NetworkManagerGeneric.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerGeneric.lo `test -f 'NetworkManagerGeneric.c' || echo '$(srcdir)/'`NetworkManagerGeneric.c
+
+libnmbackend_la-NetworkManagerRedHat.lo: NetworkManagerRedHat.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerRedHat.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerRedHat.Tpo -c -o libnmbackend_la-NetworkManagerRedHat.lo `test -f 'NetworkManagerRedHat.c' || echo '$(srcdir)/'`NetworkManagerRedHat.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerRedHat.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerRedHat.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerRedHat.c' object='libnmbackend_la-NetworkManagerRedHat.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerRedHat.lo `test -f 'NetworkManagerRedHat.c' || echo '$(srcdir)/'`NetworkManagerRedHat.c
+
+libnmbackend_la-NetworkManagerSuSE.lo: NetworkManagerSuSE.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerSuSE.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerSuSE.Tpo -c -o libnmbackend_la-NetworkManagerSuSE.lo `test -f 'NetworkManagerSuSE.c' || echo '$(srcdir)/'`NetworkManagerSuSE.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerSuSE.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerSuSE.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerSuSE.c' object='libnmbackend_la-NetworkManagerSuSE.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerSuSE.lo `test -f 'NetworkManagerSuSE.c' || echo '$(srcdir)/'`NetworkManagerSuSE.c
+
+libnmbackend_la-NetworkManagerGentoo.lo: NetworkManagerGentoo.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerGentoo.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerGentoo.Tpo -c -o libnmbackend_la-NetworkManagerGentoo.lo `test -f 'NetworkManagerGentoo.c' || echo '$(srcdir)/'`NetworkManagerGentoo.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerGentoo.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerGentoo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerGentoo.c' object='libnmbackend_la-NetworkManagerGentoo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerGentoo.lo `test -f 'NetworkManagerGentoo.c' || echo '$(srcdir)/'`NetworkManagerGentoo.c
+
+libnmbackend_la-NetworkManagerDebian.lo: NetworkManagerDebian.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerDebian.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerDebian.Tpo -c -o libnmbackend_la-NetworkManagerDebian.lo `test -f 'NetworkManagerDebian.c' || echo '$(srcdir)/'`NetworkManagerDebian.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerDebian.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerDebian.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerDebian.c' object='libnmbackend_la-NetworkManagerDebian.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerDebian.lo `test -f 'NetworkManagerDebian.c' || echo '$(srcdir)/'`NetworkManagerDebian.c
+
+libnmbackend_la-NetworkManagerSlackware.lo: NetworkManagerSlackware.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerSlackware.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerSlackware.Tpo -c -o libnmbackend_la-NetworkManagerSlackware.lo `test -f 'NetworkManagerSlackware.c' || echo '$(srcdir)/'`NetworkManagerSlackware.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerSlackware.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerSlackware.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerSlackware.c' object='libnmbackend_la-NetworkManagerSlackware.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerSlackware.lo `test -f 'NetworkManagerSlackware.c' || echo '$(srcdir)/'`NetworkManagerSlackware.c
+
+libnmbackend_la-NetworkManagerArch.lo: NetworkManagerArch.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerArch.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerArch.Tpo -c -o libnmbackend_la-NetworkManagerArch.lo `test -f 'NetworkManagerArch.c' || echo '$(srcdir)/'`NetworkManagerArch.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerArch.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerArch.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerArch.c' object='libnmbackend_la-NetworkManagerArch.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerArch.lo `test -f 'NetworkManagerArch.c' || echo '$(srcdir)/'`NetworkManagerArch.c
+
+libnmbackend_la-NetworkManagerPaldo.lo: NetworkManagerPaldo.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerPaldo.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerPaldo.Tpo -c -o libnmbackend_la-NetworkManagerPaldo.lo `test -f 'NetworkManagerPaldo.c' || echo '$(srcdir)/'`NetworkManagerPaldo.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerPaldo.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerPaldo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerPaldo.c' object='libnmbackend_la-NetworkManagerPaldo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerPaldo.lo `test -f 'NetworkManagerPaldo.c' || echo '$(srcdir)/'`NetworkManagerPaldo.c
+
+libnmbackend_la-NetworkManagerFrugalware.lo: NetworkManagerFrugalware.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerFrugalware.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerFrugalware.Tpo -c -o libnmbackend_la-NetworkManagerFrugalware.lo `test -f 'NetworkManagerFrugalware.c' || echo '$(srcdir)/'`NetworkManagerFrugalware.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerFrugalware.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerFrugalware.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerFrugalware.c' object='libnmbackend_la-NetworkManagerFrugalware.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerFrugalware.lo `test -f 'NetworkManagerFrugalware.c' || echo '$(srcdir)/'`NetworkManagerFrugalware.c
+
+libnmbackend_la-NetworkManagerMandriva.lo: NetworkManagerMandriva.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerMandriva.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerMandriva.Tpo -c -o libnmbackend_la-NetworkManagerMandriva.lo `test -f 'NetworkManagerMandriva.c' || echo '$(srcdir)/'`NetworkManagerMandriva.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerMandriva.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerMandriva.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerMandriva.c' object='libnmbackend_la-NetworkManagerMandriva.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerMandriva.lo `test -f 'NetworkManagerMandriva.c' || echo '$(srcdir)/'`NetworkManagerMandriva.c
+
+libnmbackend_la-NetworkManagerPardus.lo: NetworkManagerPardus.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnmbackend_la-NetworkManagerPardus.lo -MD -MP -MF $(DEPDIR)/libnmbackend_la-NetworkManagerPardus.Tpo -c -o libnmbackend_la-NetworkManagerPardus.lo `test -f 'NetworkManagerPardus.c' || echo '$(srcdir)/'`NetworkManagerPardus.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnmbackend_la-NetworkManagerPardus.Tpo $(DEPDIR)/libnmbackend_la-NetworkManagerPardus.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='NetworkManagerPardus.c' object='libnmbackend_la-NetworkManagerPardus.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnmbackend_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnmbackend_la-NetworkManagerPardus.lo `test -f 'NetworkManagerPardus.c' || echo '$(srcdir)/'`NetworkManagerPardus.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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-noinstLTLIBRARIES \
+	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: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 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/backends/NetworkManagerArch.c b/src/backends/NetworkManagerArch.c
new file mode 100644
index 00000000..c70e2459
--- /dev/null
+++ b/src/backends/NetworkManagerArch.c
@@ -0,0 +1,70 @@
+/* NetworkManager -- Network link manager
+ *
+ * Backend implementation for the Arch Linux distribution http://www.archlinux.org
+ * 
+ * Tor Krill <tor@krill.nu> and Will Rea <sillywilly@gmail.com>
+ *
+ * Updated by Wael Nasreddine <gandalf@siemens-mobiles.org>
+ *
+ * Updated by Valentine Sinitsyn <e_val@inbox.ru>
+ * 
+ * Heavily based on NetworkManagerDebian.c by Matthew Garrett <mjg59@srcf.ucam.org>
+ *
+ * Heavily based on NetworkManagerRedhat.c by Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 Tom Parker
+ * (C) Copyright 2004 Matthew Garrett
+ * (C) Copyright 2004 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+	nm_generic_enable_loopback ();
+}
+
+/*
+ * nm_system_update_dns
+ *
+ * Make glibc/nscd aware of any changes to the resolv.conf file by
+ * restarting nscd.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	/* Check if the daemon was already running - do not start a new instance */
+	if (g_file_test("/var/run/daemons/nscd", G_FILE_TEST_EXISTS))
+		nm_spawn_process ("/etc/rc.d/nscd restart");
+}
+
diff --git a/src/backends/NetworkManagerDebian.c b/src/backends/NetworkManagerDebian.c
new file mode 100644
index 00000000..ef9475af
--- /dev/null
+++ b/src/backends/NetworkManagerDebian.c
@@ -0,0 +1,64 @@
+/* NetworkManager -- Network link manager
+ *
+ * Matthew Garrett <mjg59@srcf.ucam.org>
+ *
+ * Heavily based on NetworkManagerRedhat.c by Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 Tom Parker
+ * (C) Copyright 2004 Matthew Garrett
+ * (C) Copyright 2004 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+	nm_spawn_process ("/sbin/ifup lo");
+}
+
+/*
+ * nm_system_update_dns
+ *
+ * Invalidate the nscd host cache, if it exists, since
+ * we changed resolv.conf.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_IS_EXECUTABLE)) {
+		nm_log_info (LOGD_DNS, "Clearing nscd hosts cache.");
+		nm_spawn_process ("/usr/sbin/nscd -i hosts");
+	}
+}
+
diff --git a/src/backends/NetworkManagerFrugalware.c b/src/backends/NetworkManagerFrugalware.c
new file mode 100644
index 00000000..dfe62921
--- /dev/null
+++ b/src/backends/NetworkManagerFrugalware.c
@@ -0,0 +1,58 @@
+/* NetworkManager -- Network link manager
+ *
+ * Implementation for the Frugalware Linux distro - http://www.frugalware.org
+ *
+ * Alex Smith <alex.extreme2@gmail.com>
+ *
+ * Based on NetworkManagerSlackware.c by Narayan Newton <narayan_newton@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2006 Alex Smith
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "nm-system.h"
+
+/* Provided by the frugalwareutils package on Frugalware */
+#include <libfwnetconfig.h> 
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+	fwnet_loup ();
+}
+
+
+/*
+ * nm_system_update_dns
+ *
+ * Make glibc/nscd aware of any changes to the resolv.conf file by
+ * restarting nscd.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	/* I'm not running nscd */
+}
+
diff --git a/src/backends/NetworkManagerGeneric.c b/src/backends/NetworkManagerGeneric.c
new file mode 100644
index 00000000..8f16066f
--- /dev/null
+++ b/src/backends/NetworkManagerGeneric.c
@@ -0,0 +1,113 @@
+/* -*- 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.
+ *
+ * (C) Copyright 2004 - 2010 Red Hat, Inc.
+ * (C) Copyright 2006 Timothee Lecomte <timothee.lecomte@ens.fr>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+#include "nm-netlink-monitor.h"
+
+/* Because of a bug in libnl, rtnl.h should be included before route.h */
+#include <netlink/route/rtnl.h>
+
+#include <netlink/route/addr.h>
+#include <netlink/netlink.h>
+
+/*
+ * nm_generic_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_generic_enable_loopback (void)
+{
+	struct nl_handle *	nlh = NULL;
+	struct rtnl_addr *	addr = NULL;
+	struct nl_addr *	nl_addr = NULL;
+	guint32			binaddr = 0;
+	int			iface_idx = -1;
+	int			err;
+
+	nm_system_device_set_up_down_with_iface ("lo", TRUE, NULL);
+
+	nlh = nm_netlink_get_default_handle ();
+	if (!nlh)
+		return;
+
+	iface_idx = nm_netlink_iface_to_index ("lo");
+	if (iface_idx < 0)
+		return;
+
+	addr = rtnl_addr_alloc ();
+	if (!addr)
+		return;
+
+	binaddr = htonl (0x7f000001); /* 127.0.0.1 */
+	nl_addr = nl_addr_build (AF_INET, &binaddr, sizeof(binaddr));
+	if (!nl_addr)
+		goto out;
+	rtnl_addr_set_local (addr, nl_addr);
+	nl_addr_put (nl_addr);
+
+	binaddr = htonl (0x7fffffff); /* 127.255.255.255 */
+	nl_addr = nl_addr_build (AF_INET, &binaddr, sizeof(binaddr));
+	if (!nl_addr)
+		goto out;
+	rtnl_addr_set_broadcast (addr, nl_addr);
+	nl_addr_put (nl_addr);
+
+	rtnl_addr_set_prefixlen (addr, 8);
+	rtnl_addr_set_ifindex (addr, iface_idx);
+	rtnl_addr_set_scope (addr, RT_SCOPE_HOST);
+	rtnl_addr_set_label (addr, "lo");
+
+	if ((err = rtnl_addr_add (nlh, addr, 0)) < 0) {
+		if (err != -EEXIST) {
+			nm_log_warn (LOGD_CORE, "error %d returned from rtnl_addr_add():\n%s", err, nl_geterror());
+		}
+	}
+out:
+	if (addr)
+		rtnl_addr_put (addr);
+}
+
+/*
+ * nm_generic_update_dns
+ *
+ * Make glibc/nscd aware of any changes to the resolv.conf file by
+ * restarting nscd.
+ *
+ */
+void nm_generic_update_dns (void)
+{
+}
+
diff --git a/src/backends/NetworkManagerGeneric.h b/src/backends/NetworkManagerGeneric.h
new file mode 100644
index 00000000..0a0a88a2
--- /dev/null
+++ b/src/backends/NetworkManagerGeneric.h
@@ -0,0 +1,30 @@
+/* NetworkManager -- Network link manager
+ *
+ * Timothee Lecomte <timothee.lecomte@ens.fr>
+ *
+ * Heavily based on NetworkManagerSystem.h by Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ */
+
+#ifndef NETWORK_MANAGER_GENERIC_H
+#define NETWORK_MANAGER_GENERIC_H
+
+void nm_generic_enable_loopback	(void);
+void nm_generic_update_dns		(void);
+
+#endif
diff --git a/src/backends/NetworkManagerGentoo.c b/src/backends/NetworkManagerGentoo.c
new file mode 100644
index 00000000..8d9e68c5
--- /dev/null
+++ b/src/backends/NetworkManagerGentoo.c
@@ -0,0 +1,66 @@
+/* NetworkManager -- Network link manager
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Dan Willemsen <dan@willemsen.us>
+ * Robert Paskowitz
+ *
+ * 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.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ * (C) Copyright 2004 Dan Willemsen
+ * (C) Copyright 2004 Robert Paskowitz
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+  /* No need to run net.lo if it is already running */
+	if (nm_spawn_process ("/etc/init.d/net.lo status") != 0)
+		nm_spawn_process("/etc/init.d/net.lo start");
+}
+
+/*
+ * nm_system_update_dns
+ *
+ * Make glibc/nscd aware of any changes to the resolv.conf file by
+ * restarting nscd. Only restart if already running.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_IS_EXECUTABLE)) {
+		nm_log_info (LOGD_DNS, "Clearing nscd hosts cache.");
+		nm_spawn_process ("/usr/sbin/nscd -i hosts");
+	}
+}
+
diff --git a/src/backends/NetworkManagerMandriva.c b/src/backends/NetworkManagerMandriva.c
new file mode 100644
index 00000000..8209ff4f
--- /dev/null
+++ b/src/backends/NetworkManagerMandriva.c
@@ -0,0 +1,61 @@
+/* NetworkManager -- Network link manager
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Per Øyvind Karlsen <peroyvind@mandriva.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ * (C) Copyright 2008 Per Øyvind Karlsen
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+	nm_generic_enable_loopback ();
+}
+
+/*
+ * nm_system_update_dns
+ *
+ * Invalidate the nscd host cache, if it exists, since
+ * we changed resolv.conf.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE | G_FILE_TEST_IS_REGULAR)) {
+		nm_spawn_process ("/etc/init.d/nscd condrestart");
+		nm_spawn_process ("/usr/sbin/nscd -i hosts");
+	}
+}
+
diff --git a/src/backends/NetworkManagerPaldo.c b/src/backends/NetworkManagerPaldo.c
new file mode 100644
index 00000000..0c67e5bd
--- /dev/null
+++ b/src/backends/NetworkManagerPaldo.c
@@ -0,0 +1,61 @@
+/* NetworkManager -- Network link manager
+ *
+ * Jürg Billeter <juerg@paldo.org>
+ *
+ * Heavily based on NetworkManagerRedhat.c by Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ * (C) Copyright 2006 Jürg Billeter
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+	nm_generic_enable_loopback ();
+}
+
+/*
+ * nm_system_update_dns
+ *
+ * Invalidate the nscd host cache, if it exists, since
+ * we changed resolv.conf.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	nm_log_info (LOGD_DNS, "Clearing nscd hosts cache.");
+	nm_spawn_process ("/usr/sbin/nscd -i hosts");
+}
+
diff --git a/src/backends/NetworkManagerPardus.c b/src/backends/NetworkManagerPardus.c
new file mode 100644
index 00000000..d952cf36
--- /dev/null
+++ b/src/backends/NetworkManagerPardus.c
@@ -0,0 +1,57 @@
+/* NetworkManager -- Network link manager
+ *
+ * Ozan Caglayan <ozan@pardus.org.tr>
+ *
+ * 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.
+ *
+ * (C) Copyright 2004 RedHat, Inc.
+ * (C) Copyright 2009 TUBITAK/UEKAE
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+	nm_generic_enable_loopback ();
+}
+
+
+/*
+ * nm_system_update_dns
+ *
+ * Make glibc/nscd aware of any changes to the resolv.conf file by
+ * restarting nscd.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	/* I'm not running nscd */
+}
+
diff --git a/src/backends/NetworkManagerRedHat.c b/src/backends/NetworkManagerRedHat.c
new file mode 100644
index 00000000..954e58a8
--- /dev/null
+++ b/src/backends/NetworkManagerRedHat.c
@@ -0,0 +1,59 @@
+/* NetworkManager -- Network link manager
+ *
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+	nm_generic_enable_loopback ();
+}
+
+/*
+ * nm_system_update_dns
+ *
+ * Invalidate the nscd host cache, if it exists, since
+ * we changed resolv.conf.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE | G_FILE_TEST_IS_REGULAR)) {
+		nm_spawn_process ("/etc/init.d/nscd condrestart");
+		nm_spawn_process ("/usr/sbin/nscd -i hosts");
+	}
+}
+
diff --git a/src/backends/NetworkManagerSlackware.c b/src/backends/NetworkManagerSlackware.c
new file mode 100644
index 00000000..a35e1bb6
--- /dev/null
+++ b/src/backends/NetworkManagerSlackware.c
@@ -0,0 +1,57 @@
+/* NetworkManager -- Network link manager
+ *
+ * Narayan Newton <narayan_newton@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 RedHat, Inc.
+ * (C) Copyright 2004 Narayan Newton
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+	nm_generic_enable_loopback ();
+}
+
+
+/*
+ * nm_system_update_dns
+ *
+ * Make glibc/nscd aware of any changes to the resolv.conf file by
+ * restarting nscd.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	/* I'm not running nscd */
+}
+
diff --git a/src/backends/NetworkManagerSuSE.c b/src/backends/NetworkManagerSuSE.c
new file mode 100644
index 00000000..6c8ec1a5
--- /dev/null
+++ b/src/backends/NetworkManagerSuSE.c
@@ -0,0 +1,62 @@
+/*
+ * NetworkManager -- Network link manager
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Kay Sievers <kay.sievers@suse.de>
+ * Robert Love <rml@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ * (C) Copyright 2005-2006 SuSE GmbH
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+	nm_generic_enable_loopback ();
+}
+
+/*
+ * nm_system_update_dns
+ *
+ * Invalidate the nscd host cache, if it exists, since
+ * we changed resolv.conf.
+ *
+ */
+void nm_system_update_dns (void)
+{
+	nm_log_info (LOGD_DNS, "Clearing nscd hosts cache.");
+	nm_spawn_process ("/usr/sbin/nscd -i hosts");
+}
+
diff --git a/src/bluez-manager/Makefile.am b/src/bluez-manager/Makefile.am
new file mode 100644
index 00000000..6c199e8c
--- /dev/null
+++ b/src/bluez-manager/Makefile.am
@@ -0,0 +1,38 @@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_builddir}/marshallers \
+	-I$(top_srcdir)/src/nm-bluez-manager
+
+noinst_LTLIBRARIES = libbluez-manager.la
+
+libbluez_manager_la_SOURCES = \
+	nm-bluez-manager.c \
+	nm-bluez-manager.h \
+	nm-bluez-adapter.c \
+	nm-bluez-adapter.h \
+	nm-bluez-device.c \
+	nm-bluez-device.h \
+	nm-bluez-common.h
+
+libbluez_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(BLUEZ_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\"
+
+libbluez_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS) \
+	$(BLUEZ_LIBS)
+
diff --git a/src/bluez-manager/Makefile.in b/src/bluez-manager/Makefile.in
new file mode 100644
index 00000000..037f9fc9
--- /dev/null
+++ b/src/bluez-manager/Makefile.in
@@ -0,0 +1,601 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/bluez-manager
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libbluez_manager_la_DEPENDENCIES =  \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libbluez_manager_la_OBJECTS =  \
+	libbluez_manager_la-nm-bluez-manager.lo \
+	libbluez_manager_la-nm-bluez-adapter.lo \
+	libbluez_manager_la-nm-bluez-device.lo
+libbluez_manager_la_OBJECTS = $(am_libbluez_manager_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libbluez_manager_la_SOURCES)
+DIST_SOURCES = $(libbluez_manager_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_builddir}/marshallers \
+	-I$(top_srcdir)/src/nm-bluez-manager
+
+noinst_LTLIBRARIES = libbluez-manager.la
+libbluez_manager_la_SOURCES = \
+	nm-bluez-manager.c \
+	nm-bluez-manager.h \
+	nm-bluez-adapter.c \
+	nm-bluez-adapter.h \
+	nm-bluez-device.c \
+	nm-bluez-device.h \
+	nm-bluez-common.h
+
+libbluez_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(BLUEZ_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\"
+
+libbluez_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS) \
+	$(BLUEZ_LIBS)
+
+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/bluez-manager/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/bluez-manager/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libbluez-manager.la: $(libbluez_manager_la_OBJECTS) $(libbluez_manager_la_DEPENDENCIES) 
+	$(LINK)  $(libbluez_manager_la_OBJECTS) $(libbluez_manager_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbluez_manager_la-nm-bluez-adapter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbluez_manager_la-nm-bluez-device.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbluez_manager_la-nm-bluez-manager.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libbluez_manager_la-nm-bluez-manager.lo: nm-bluez-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libbluez_manager_la-nm-bluez-manager.lo -MD -MP -MF $(DEPDIR)/libbluez_manager_la-nm-bluez-manager.Tpo -c -o libbluez_manager_la-nm-bluez-manager.lo `test -f 'nm-bluez-manager.c' || echo '$(srcdir)/'`nm-bluez-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libbluez_manager_la-nm-bluez-manager.Tpo $(DEPDIR)/libbluez_manager_la-nm-bluez-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-bluez-manager.c' object='libbluez_manager_la-nm-bluez-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libbluez_manager_la-nm-bluez-manager.lo `test -f 'nm-bluez-manager.c' || echo '$(srcdir)/'`nm-bluez-manager.c
+
+libbluez_manager_la-nm-bluez-adapter.lo: nm-bluez-adapter.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libbluez_manager_la-nm-bluez-adapter.lo -MD -MP -MF $(DEPDIR)/libbluez_manager_la-nm-bluez-adapter.Tpo -c -o libbluez_manager_la-nm-bluez-adapter.lo `test -f 'nm-bluez-adapter.c' || echo '$(srcdir)/'`nm-bluez-adapter.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libbluez_manager_la-nm-bluez-adapter.Tpo $(DEPDIR)/libbluez_manager_la-nm-bluez-adapter.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-bluez-adapter.c' object='libbluez_manager_la-nm-bluez-adapter.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libbluez_manager_la-nm-bluez-adapter.lo `test -f 'nm-bluez-adapter.c' || echo '$(srcdir)/'`nm-bluez-adapter.c
+
+libbluez_manager_la-nm-bluez-device.lo: nm-bluez-device.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libbluez_manager_la-nm-bluez-device.lo -MD -MP -MF $(DEPDIR)/libbluez_manager_la-nm-bluez-device.Tpo -c -o libbluez_manager_la-nm-bluez-device.lo `test -f 'nm-bluez-device.c' || echo '$(srcdir)/'`nm-bluez-device.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libbluez_manager_la-nm-bluez-device.Tpo $(DEPDIR)/libbluez_manager_la-nm-bluez-device.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-bluez-device.c' object='libbluez_manager_la-nm-bluez-device.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbluez_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libbluez_manager_la-nm-bluez-device.lo `test -f 'nm-bluez-device.c' || echo '$(srcdir)/'`nm-bluez-device.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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-noinstLTLIBRARIES \
+	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: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 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/bluez-manager/nm-bluez-adapter.c b/src/bluez-manager/nm-bluez-adapter.c
new file mode 100644
index 00000000..27a85e78
--- /dev/null
+++ b/src/bluez-manager/nm-bluez-adapter.c
@@ -0,0 +1,367 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "NetworkManager.h"
+#include "nm-dbus-manager.h"
+#include "nm-bluez-adapter.h"
+#include "nm-bluez-device.h"
+#include "nm-bluez-common.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-logging.h"
+
+
+G_DEFINE_TYPE (NMBluezAdapter, nm_bluez_adapter, G_TYPE_OBJECT)
+
+#define NM_BLUEZ_ADAPTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_ADAPTER, NMBluezAdapterPrivate))
+
+typedef struct {
+	char *path;
+	DBusGProxy *proxy;
+	gboolean initialized;
+
+	char *address;
+	GHashTable *devices;
+} NMBluezAdapterPrivate;
+
+
+enum {
+	PROP_0,
+	PROP_PATH,
+	PROP_ADDRESS,
+
+	LAST_PROP
+};
+
+/* Signals */
+enum {
+	INITIALIZED,
+	DEVICE_ADDED,
+	DEVICE_REMOVED,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+const char *
+nm_bluez_adapter_get_path (NMBluezAdapter *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_ADAPTER (self), NULL);
+
+	return NM_BLUEZ_ADAPTER_GET_PRIVATE (self)->path;
+}
+
+const char *
+nm_bluez_adapter_get_address (NMBluezAdapter *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_ADAPTER (self), NULL);
+
+	return NM_BLUEZ_ADAPTER_GET_PRIVATE (self)->address;
+}
+
+gboolean
+nm_bluez_adapter_get_initialized (NMBluezAdapter *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_ADAPTER (self), FALSE);
+
+	return NM_BLUEZ_ADAPTER_GET_PRIVATE (self)->initialized;
+}
+
+static void
+devices_to_list (gpointer key, gpointer data, gpointer user_data)
+{
+	NMBluezDevice *device = NM_BLUEZ_DEVICE (data);
+	GSList **list = user_data;
+
+	if (nm_bluez_device_get_usable (device))
+		*list = g_slist_append (*list, data);
+}
+
+GSList *
+nm_bluez_adapter_get_devices (NMBluezAdapter *self)
+{
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self);
+	GSList *devices = NULL;
+
+	g_hash_table_foreach (priv->devices, devices_to_list, &devices);
+	return devices;
+}
+
+static void
+device_usable (NMBluezDevice *device, GParamSpec *pspec, gpointer user_data)
+{
+	NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data);
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self);
+
+	if (nm_bluez_device_get_usable (device))
+		g_signal_emit (self, signals[DEVICE_ADDED], 0, device);
+	else {
+		g_object_ref (device);
+		g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device));
+		g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
+		g_object_unref (device);
+	}
+}
+
+static void
+device_initialized (NMBluezDevice *device, gboolean success, gpointer user_data)
+{
+	NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data);
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self);
+
+	if (!success)
+		g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device));
+}
+
+static void
+device_created (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+	NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data);
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self);
+	NMBluezDevice *device;
+
+	device = nm_bluez_device_new (path);
+	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
+	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
+	g_hash_table_insert (priv->devices, g_strdup (path), device);
+}
+
+static void
+device_removed (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+	NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data);
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self);
+	NMBluezDevice *device;
+
+	device = g_hash_table_lookup (priv->devices, path);
+	if (device) {
+		g_object_ref (device);
+		g_hash_table_remove (priv->devices, path);
+		g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
+		g_object_unref (device);
+	}
+}
+
+
+static void
+get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NMBluezAdapter *self = NM_BLUEZ_ADAPTER (user_data);
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self);
+	GHashTable *properties = NULL;
+	GError *err = NULL;
+	GValue *value;
+	GPtrArray *devices;
+	int i;
+
+	if (!dbus_g_proxy_end_call (proxy, call, &err,
+	                            DBUS_TYPE_G_MAP_OF_VARIANT, &properties,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_BT, "bluez error getting adapter properties: %s",
+		             err && err->message ? err->message : "(unknown)");
+		g_error_free (err);
+		goto done;
+	}
+
+	value = g_hash_table_lookup (properties, "Address");
+	priv->address = value ? g_value_dup_string (value) : NULL;
+
+	value = g_hash_table_lookup (properties, "Devices");
+	devices = value ? g_value_get_boxed (value) : NULL;
+
+	for (i = 0; devices && i < devices->len; i++)
+ 		device_created (priv->proxy, g_ptr_array_index (devices, i), self);
+
+	g_hash_table_unref (properties);
+
+	priv->initialized = TRUE;
+
+done:
+	g_signal_emit (self, signals[INITIALIZED], 0, priv->initialized);
+}
+
+static void
+query_properties (NMBluezAdapter *self)
+{
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self);
+	DBusGProxyCall *call;
+
+	call = dbus_g_proxy_begin_call (priv->proxy, "GetProperties",
+	                                get_properties_cb,
+	                                self,
+	                                NULL, G_TYPE_INVALID);
+	if (!call) {
+		nm_log_warn (LOGD_BT, "failed to request Bluetooth adapter properties for %s.",
+		             priv->path);
+	}
+}
+
+NMBluezAdapter *
+nm_bluez_adapter_new (const char *path)
+{
+	NMBluezAdapter *self;
+	NMBluezAdapterPrivate *priv;
+	NMDBusManager *dbus_mgr;
+	DBusGConnection *connection;
+
+	self = (NMBluezAdapter *) g_object_new (NM_TYPE_BLUEZ_ADAPTER,
+	                                        NM_BLUEZ_ADAPTER_PATH, path,
+	                                        NULL);
+	if (!self)
+		return NULL;
+
+	priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self);
+	dbus_mgr = nm_dbus_manager_get ();
+	connection = nm_dbus_manager_get_connection (dbus_mgr);
+
+	priv->proxy = dbus_g_proxy_new_for_name (connection,
+	                                         BLUEZ_SERVICE,
+	                                         priv->path,
+	                                         BLUEZ_ADAPTER_INTERFACE);
+	g_object_unref (dbus_mgr);
+
+	dbus_g_proxy_add_signal (priv->proxy, "DeviceCreated",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "DeviceCreated",
+	                             G_CALLBACK (device_created), self, NULL);
+
+	dbus_g_proxy_add_signal (priv->proxy, "DeviceRemoved",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "DeviceRemoved",
+	                             G_CALLBACK (device_removed), self, NULL);
+
+	query_properties (self);
+	return self;
+}
+
+static void
+nm_bluez_adapter_init (NMBluezAdapter *self)
+{
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (self);
+
+	priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                       g_free, g_object_unref);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->devices);
+	g_free (priv->address);
+	g_free (priv->path);
+	g_object_unref (priv->proxy);
+
+	G_OBJECT_CLASS (nm_bluez_adapter_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		g_value_set_string (value, priv->path);
+		break;
+	case PROP_ADDRESS:
+		g_value_set_string (value, priv->address);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMBluezAdapterPrivate *priv = NM_BLUEZ_ADAPTER_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		/* construct only */
+		priv->path = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_bluez_adapter_class_init (NMBluezAdapterClass *config_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+	g_type_class_add_private (config_class, sizeof (NMBluezAdapterPrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->finalize = finalize;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_PATH,
+		 g_param_spec_string (NM_BLUEZ_ADAPTER_PATH,
+		                      "DBus Path",
+		                      "DBus Path",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_ADDRESS,
+		 g_param_spec_string (NM_BLUEZ_ADAPTER_ADDRESS,
+		                      "Address",
+		                      "Address",
+		                      NULL,
+		                      G_PARAM_READABLE));
+
+	/* Signals */
+	signals[INITIALIZED] = g_signal_new ("initialized",
+	                                     G_OBJECT_CLASS_TYPE (object_class),
+	                                     G_SIGNAL_RUN_LAST,
+	                                     G_STRUCT_OFFSET (NMBluezAdapterClass, initialized),
+	                                     NULL, NULL,
+	                                     g_cclosure_marshal_VOID__BOOLEAN,
+	                                     G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+	signals[DEVICE_ADDED] = g_signal_new ("device-added",
+	                                      G_OBJECT_CLASS_TYPE (object_class),
+	                                      G_SIGNAL_RUN_LAST,
+	                                      G_STRUCT_OFFSET (NMBluezAdapterClass, device_added),
+	                                      NULL, NULL,
+	                                      g_cclosure_marshal_VOID__OBJECT,
+	                                      G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+	signals[DEVICE_REMOVED] = g_signal_new ("device-removed",
+	                                        G_OBJECT_CLASS_TYPE (object_class),
+	                                        G_SIGNAL_RUN_LAST,
+	                                        G_STRUCT_OFFSET (NMBluezAdapterClass, device_removed),
+	                                        NULL, NULL,
+	                                        g_cclosure_marshal_VOID__OBJECT,
+	                                        G_TYPE_NONE, 1, G_TYPE_OBJECT);
+}
+
diff --git a/src/bluez-manager/nm-bluez-adapter.h b/src/bluez-manager/nm-bluez-adapter.h
new file mode 100644
index 00000000..c552e989
--- /dev/null
+++ b/src/bluez-manager/nm-bluez-adapter.h
@@ -0,0 +1,67 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ_ADAPTER_H
+#define NM_BLUEZ_ADAPTER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-bluez-device.h"
+
+#define NM_TYPE_BLUEZ_ADAPTER            (nm_bluez_adapter_get_type ())
+#define NM_BLUEZ_ADAPTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_ADAPTER, NMBluezAdapter))
+#define NM_BLUEZ_ADAPTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_ADAPTER, NMBluezAdapterClass))
+#define NM_IS_BLUEZ_ADAPTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_ADAPTER))
+#define NM_IS_BLUEZ_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_BLUEZ_ADAPTER))
+#define NM_BLUEZ_ADAPTER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_ADAPTER, NMBluezAdapterClass))
+
+#define NM_BLUEZ_ADAPTER_PATH    "path"
+#define NM_BLUEZ_ADAPTER_ADDRESS "address"
+
+typedef struct {
+	GObject parent;
+} NMBluezAdapter;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* virtual functions */
+	void (*initialized)    (NMBluezAdapter *self, gboolean success);
+
+	void (*device_added)   (NMBluezAdapter *self, NMBluezDevice *device);
+
+	void (*device_removed) (NMBluezAdapter *self, NMBluezDevice *device);
+} NMBluezAdapterClass;
+
+GType nm_bluez_adapter_get_type (void);
+
+NMBluezAdapter *nm_bluez_adapter_new (const char *path);
+
+const char *nm_bluez_adapter_get_path (NMBluezAdapter *self);
+
+const char *nm_bluez_adapter_get_address (NMBluezAdapter *self);
+
+gboolean nm_bluez_adapter_get_initialized (NMBluezAdapter *self);
+
+GSList *nm_bluez_adapter_get_devices (NMBluezAdapter *self);
+
+#endif /* NM_BLUEZ_ADAPTER_H */
+
diff --git a/src/bluez-manager/nm-bluez-common.h b/src/bluez-manager/nm-bluez-common.h
new file mode 100644
index 00000000..61296255
--- /dev/null
+++ b/src/bluez-manager/nm-bluez-common.h
@@ -0,0 +1,34 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ_COMMON_H
+#define NM_BLUEZ_COMMON_H
+
+#define BLUEZ_SERVICE           "org.bluez"
+
+#define BLUEZ_MANAGER_PATH      "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE  "org.bluez.Device"
+#define BLUEZ_SERIAL_INTERFACE  "org.bluez.Serial"
+#define BLUEZ_NETWORK_INTERFACE "org.bluez.Network"
+
+#endif  /* NM_BLUEZ_COMMON_H */
+
diff --git a/src/bluez-manager/nm-bluez-device.c b/src/bluez-manager/nm-bluez-device.c
new file mode 100644
index 00000000..72acaa69
--- /dev/null
+++ b/src/bluez-manager/nm-bluez-device.c
@@ -0,0 +1,452 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "NetworkManager.h"
+#include "nm-dbus-manager.h"
+#include "nm-bluez-device.h"
+#include "nm-bluez-common.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-logging.h"
+#include "nm-marshal.h"
+
+
+G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT)
+
+#define NM_BLUEZ_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_DEVICE, NMBluezDevicePrivate))
+
+typedef struct {
+	char *path;
+	DBusGProxy *proxy;
+	gboolean initialized;
+	gboolean usable;
+
+	char *address;
+	char *name;
+	guint32 capabilities;
+	gint rssi;
+} NMBluezDevicePrivate;
+
+
+enum {
+	PROP_0,
+	PROP_PATH,
+	PROP_ADDRESS,
+	PROP_NAME,
+	PROP_CAPABILITIES,
+	PROP_RSSI,
+	PROP_USABLE,
+
+	LAST_PROP
+};
+
+/* Signals */
+enum {
+	INITIALIZED,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+const char *
+nm_bluez_device_get_path (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path;
+}
+
+const char *
+nm_bluez_device_get_address (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->address;
+}
+
+gboolean
+nm_bluez_device_get_initialized (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->initialized;
+}
+
+gboolean
+nm_bluez_device_get_usable (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->usable;
+}
+
+const char *
+nm_bluez_device_get_name (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->name;
+}
+
+guint32
+nm_bluez_device_get_capabilities (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->capabilities;
+}
+
+gint
+nm_bluez_device_get_rssi (NMBluezDevice *self)
+{
+	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0);
+
+	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->rssi;
+}
+
+static guint32
+convert_uuids_to_capabilities (const char **strings)
+{
+	const char **iter;
+	guint32 capabilities = 0;
+
+	for (iter = strings; iter && *iter; iter++) {
+		char **parts;
+		guint uuid16;
+
+		parts = g_strsplit (*iter, "-", -1);
+		if (parts == NULL || parts[0] == NULL) {
+			g_strfreev (parts);
+			continue;
+		}
+
+		uuid16 = g_ascii_strtoull (parts[0], NULL, 16);
+		g_strfreev (parts);
+
+		switch (uuid16) {
+		case 0x1103:
+			capabilities |= NM_BT_CAPABILITY_DUN;
+			break;
+		case 0x1116:
+			capabilities |= NM_BT_CAPABILITY_NAP;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return capabilities;
+}
+
+static void
+check_emit_usable (NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+	if (   priv->initialized
+	    && priv->capabilities
+	    && priv->name
+	    && priv->address) {
+		if (!priv->usable) {
+			priv->usable = TRUE;
+			g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_USABLE);
+		}
+	} else {
+		if (priv->usable) {
+			priv->usable = FALSE;
+			g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_USABLE);
+		}
+	}
+}
+
+static void
+property_changed (DBusGProxy *proxy,
+                  const char *property,
+                  GValue *value,
+                  gpointer user_data)
+{
+	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	const char *str;
+	guint32 uint_val;
+	gint int_val;
+
+	if (!strcmp (property, "Name")) {
+		str = g_value_get_string (value);
+		if (   (!priv->name && str)
+		    || (priv->name && !str)
+		    || (priv->name && str && strcmp (priv->name, str))) {
+			g_free (priv->name);
+			priv->name = str ? g_strdup (str) : NULL;
+			g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_NAME);
+		}
+	} else if (!strcmp (property, "RSSI")) {
+		int_val = g_value_get_int (value);
+		if (priv->rssi != int_val) {
+			priv->rssi = int_val;
+			g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_RSSI);
+		}
+	} else if (!strcmp (property, "UUIDs")) {
+		uint_val = convert_uuids_to_capabilities ((const char **) g_value_get_boxed (value));
+		if (priv->capabilities != uint_val) {
+			priv->capabilities = uint_val;
+			g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CAPABILITIES);
+		}
+	}
+
+	check_emit_usable (self);
+}
+
+static void
+get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	GHashTable *properties = NULL;
+	GError *err = NULL;
+	GValue *value;
+	const char **uuids;
+
+	if (!dbus_g_proxy_end_call (proxy, call, &err,
+	                            DBUS_TYPE_G_MAP_OF_VARIANT, &properties,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_BT, "bluez error getting device properties: %s",
+		             err && err->message ? err->message : "(unknown)");
+		g_error_free (err);
+		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
+		return;
+	}
+
+	value = g_hash_table_lookup (properties, "Address");
+	priv->address = value ? g_value_dup_string (value) : NULL;
+
+	value = g_hash_table_lookup (properties, "Name");
+	priv->name = value ? g_value_dup_string (value) : NULL;
+
+	value = g_hash_table_lookup (properties, "RSSI");
+	priv->rssi = value ? g_value_get_int (value) : 0;
+
+	value = g_hash_table_lookup (properties, "UUIDs");
+	if (value) {
+		uuids = (const char **) g_value_get_boxed (value);
+		priv->capabilities = convert_uuids_to_capabilities (uuids);
+	} else
+		priv->capabilities = NM_BT_CAPABILITY_NONE;
+
+	g_hash_table_unref (properties);
+
+	priv->initialized = TRUE;
+	g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
+
+	check_emit_usable (self);
+}
+
+static void
+query_properties (NMBluezDevice *self)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	DBusGProxyCall *call;
+
+	call = dbus_g_proxy_begin_call (priv->proxy, "GetProperties",
+	                                get_properties_cb,
+	                                self,
+	                                NULL, G_TYPE_INVALID);
+	if (!call) {
+		nm_log_warn (LOGD_BT, "failed to request Bluetooth device properties for %s.",
+		             priv->path);
+	}
+}
+
+NMBluezDevice *
+nm_bluez_device_new (const char *path)
+{
+	NMBluezDevice *self;
+	NMBluezDevicePrivate *priv;
+	NMDBusManager *dbus_mgr;
+	DBusGConnection *connection;
+
+
+	self = (NMBluezDevice *) g_object_new (NM_TYPE_BLUEZ_DEVICE,
+	                                       NM_BLUEZ_DEVICE_PATH, path,
+	                                       NULL);
+	if (!self)
+		return NULL;
+
+	priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	dbus_mgr = nm_dbus_manager_get ();
+	connection = nm_dbus_manager_get_connection (dbus_mgr);
+
+	priv->proxy = dbus_g_proxy_new_for_name (connection,
+	                                         BLUEZ_SERVICE,
+	                                         priv->path,
+	                                         BLUEZ_DEVICE_INTERFACE);
+	g_object_unref (dbus_mgr);
+
+	dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED,
+	                                   G_TYPE_NONE,
+	                                   G_TYPE_STRING, G_TYPE_VALUE,
+	                                   G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->proxy, "PropertyChanged",
+	                         G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "PropertyChanged",
+	                             G_CALLBACK (property_changed), self, NULL);
+
+	query_properties (self);
+	return self;
+}
+
+static void
+nm_bluez_device_init (NMBluezDevice *self)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object);
+
+	g_free (priv->path);
+	g_free (priv->address);
+	g_free (priv->name);
+	g_object_unref (priv->proxy);
+
+	G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		g_value_set_string (value, priv->path);
+		break;
+	case PROP_ADDRESS:
+		g_value_set_string (value, priv->address);
+		break;
+	case PROP_NAME:
+		g_value_set_string (value, priv->name);
+		break;
+	case PROP_CAPABILITIES:
+		g_value_set_uint (value, priv->capabilities);
+		break;
+	case PROP_RSSI:
+		g_value_set_int (value, priv->rssi);
+		break;
+	case PROP_USABLE:
+		g_value_set_boolean (value, priv->usable);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		/* construct only */
+		priv->path = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_bluez_device_class_init (NMBluezDeviceClass *config_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+	g_type_class_add_private (config_class, sizeof (NMBluezDevicePrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->finalize = finalize;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_PATH,
+		 g_param_spec_string (NM_BLUEZ_DEVICE_PATH,
+		                      "DBus Path",
+		                      "DBus Path",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_ADDRESS,
+		 g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS,
+		                      "Address",
+		                      "Address",
+		                      NULL,
+		                      G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_NAME,
+		 g_param_spec_string (NM_BLUEZ_DEVICE_NAME,
+		                      "Name",
+		                      "Name",
+		                      NULL,
+		                      G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_CAPABILITIES,
+		 g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES,
+		                      "Capabilities",
+		                      "Capabilities",
+		                      0, G_MAXUINT, 0,
+		                      G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_RSSI,
+		 g_param_spec_int (NM_BLUEZ_DEVICE_RSSI,
+		                      "RSSI",
+		                      "RSSI",
+		                      G_MININT, G_MAXINT, 0,
+		                      G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_USABLE,
+		 g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE,
+		                       "Usable",
+		                       "Usable",
+		                       FALSE,
+		                       G_PARAM_READABLE));
+
+	/* Signals */
+	signals[INITIALIZED] = g_signal_new ("initialized",
+	                                     G_OBJECT_CLASS_TYPE (object_class),
+	                                     G_SIGNAL_RUN_LAST,
+	                                     G_STRUCT_OFFSET (NMBluezDeviceClass, initialized),
+	                                     NULL, NULL,
+	                                     g_cclosure_marshal_VOID__BOOLEAN,
+	                                     G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
diff --git a/src/bluez-manager/nm-bluez-device.h b/src/bluez-manager/nm-bluez-device.h
new file mode 100644
index 00000000..8a6b3597
--- /dev/null
+++ b/src/bluez-manager/nm-bluez-device.h
@@ -0,0 +1,75 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ_DEVICE_H
+#define NM_BLUEZ_DEVICE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define NM_TYPE_BLUEZ_DEVICE            (nm_bluez_device_get_type ())
+#define NM_BLUEZ_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDevice))
+#define NM_BLUEZ_DEVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
+#define NM_IS_BLUEZ_DEVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_DEVICE))
+#define NM_IS_BLUEZ_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_BLUEZ_DEVICE))
+#define NM_BLUEZ_DEVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
+
+#define NM_BLUEZ_DEVICE_PATH         "path"
+#define NM_BLUEZ_DEVICE_ADDRESS      "address"
+#define NM_BLUEZ_DEVICE_NAME         "name"
+#define NM_BLUEZ_DEVICE_CAPABILITIES "capabilities"
+#define NM_BLUEZ_DEVICE_RSSI         "rssi"
+#define NM_BLUEZ_DEVICE_USABLE       "usable"
+
+typedef struct {
+	GObject parent;
+} NMBluezDevice;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* virtual functions */
+	void (*initialized) (NMBluezDevice *self, gboolean success);
+
+	void (*invalid)     (NMBluezDevice *self);
+} NMBluezDeviceClass;
+
+GType nm_bluez_device_get_type (void);
+
+NMBluezDevice *nm_bluez_device_new (const char *path);
+
+const char *nm_bluez_device_get_path (NMBluezDevice *self);
+
+gboolean nm_bluez_device_get_initialized (NMBluezDevice *self);
+
+gboolean nm_bluez_device_get_usable (NMBluezDevice *self);
+
+const char *nm_bluez_device_get_address (NMBluezDevice *self);
+
+const char *nm_bluez_device_get_name (NMBluezDevice *self);
+
+guint32 nm_bluez_device_get_class (NMBluezDevice *self);
+
+guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self);
+
+gint nm_bluez_device_get_rssi (NMBluezDevice *self);
+
+#endif /* NM_BLUEZ_DEVICE_H */
+
diff --git a/src/bluez-manager/nm-bluez-manager.c b/src/bluez-manager/nm-bluez-manager.c
new file mode 100644
index 00000000..fe027c4f
--- /dev/null
+++ b/src/bluez-manager/nm-bluez-manager.c
@@ -0,0 +1,369 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2010 Red Hat, Inc.
+ */
+
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dbus/dbus-glib.h>
+
+#include "nm-logging.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-marshal.h"
+#include "nm-bluez-manager.h"
+#include "nm-bluez-adapter.h"
+#include "nm-dbus-manager.h"
+#include "nm-bluez-common.h"
+
+
+typedef struct {
+	NMDBusManager *dbus_mgr;
+	gulong name_owner_changed_id;
+
+	DBusGProxy *proxy;
+
+	NMBluezAdapter *adapter;
+} NMBluezManagerPrivate;
+
+#define NM_BLUEZ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerPrivate))
+
+G_DEFINE_TYPE (NMBluezManager, nm_bluez_manager, G_TYPE_OBJECT)
+
+enum {
+	BDADDR_ADDED,
+	BDADDR_REMOVED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+
+emit_bdaddr_added (NMBluezManager *self, NMBluezDevice *device)
+{
+	g_signal_emit (self, signals[BDADDR_ADDED], 0,
+	               nm_bluez_device_get_address (device),
+	               nm_bluez_device_get_name (device),
+	               nm_bluez_device_get_path (device),
+	               nm_bluez_device_get_capabilities (device));
+}
+
+void
+nm_bluez_manager_query_devices (NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	GSList *devices, *iter;
+
+	if (!priv->adapter)
+		return;
+
+	devices = nm_bluez_adapter_get_devices (priv->adapter);
+	for (iter = devices; iter; iter = g_slist_next (iter))
+		emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data));
+	g_slist_free (devices);
+}
+
+static void
+device_added (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data)
+{
+	emit_bdaddr_added (NM_BLUEZ_MANAGER (user_data), device);
+}
+
+static void
+device_removed (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data)
+{
+	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
+
+	g_signal_emit (self, signals[BDADDR_REMOVED], 0,
+	               nm_bluez_device_get_address (device),
+	               nm_bluez_device_get_path (device));
+}
+
+static void
+adapter_initialized (NMBluezAdapter *adapter, gboolean success, gpointer user_data)
+{
+	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	if (success) {
+		GSList *devices, *iter;
+
+		devices = nm_bluez_adapter_get_devices (adapter);
+		for (iter = devices; iter; iter = g_slist_next (iter))
+			emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data));
+		g_slist_free (devices);
+
+		g_signal_connect (adapter, "device-added", G_CALLBACK (device_added), self);
+		g_signal_connect (adapter, "device-removed", G_CALLBACK (device_removed), self);
+	} else {
+		g_object_unref (priv->adapter);
+		priv->adapter = NULL;
+	}
+}
+
+static void
+adapter_removed (DBusGProxy *proxy, const char *path, NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	if (priv->adapter && !strcmp (path, nm_bluez_adapter_get_path (priv->adapter))) {
+		if (nm_bluez_adapter_get_initialized (priv->adapter)) {
+			GSList *devices, *iter;
+
+			devices = nm_bluez_adapter_get_devices (priv->adapter);
+			for (iter = devices; iter; iter = g_slist_next (iter)) {
+				NMBluezDevice *device = NM_BLUEZ_DEVICE (iter->data);
+
+				g_signal_emit (self, signals[BDADDR_REMOVED], 0,
+				               nm_bluez_device_get_address (device),
+				               nm_bluez_device_get_path (device));
+			}
+			g_slist_free (devices);
+		}
+
+		g_object_unref (priv->adapter);
+		priv->adapter = NULL;
+	}
+}
+
+static void
+default_adapter_changed (DBusGProxy *proxy, const char *path, NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	const char *cur_path = NULL;
+
+	if (priv->adapter)
+		cur_path = nm_bluez_adapter_get_path (priv->adapter);
+
+	if (cur_path) {
+		if (!path || strcmp (path, cur_path)) {
+			/* Default adapter changed */
+			adapter_removed (priv->proxy, cur_path, self);
+		} else {
+			/* This adapter is already the default */
+			return;
+		}
+	}
+
+	/* Add the new default adapter */
+	if (path) {
+		priv->adapter = nm_bluez_adapter_new (path);
+		g_signal_connect (priv->adapter, "initialized", G_CALLBACK (adapter_initialized), self);
+	}
+}
+
+static void
+default_adapter_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	const char *default_adapter = NULL;
+	GError *err = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call, &err,
+	                            DBUS_TYPE_G_OBJECT_PATH, &default_adapter,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_BT, "bluez error getting default adapter: %s",
+		             err && err->message ? err->message : "(unknown)");
+		g_error_free (err);
+		return;
+	}
+
+	default_adapter_changed (priv->proxy, default_adapter, self);
+}
+
+static void
+query_default_adapter (NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	DBusGProxyCall *call;
+
+	call = dbus_g_proxy_begin_call (priv->proxy, "DefaultAdapter",
+	                                default_adapter_cb,
+	                                self,
+	                                NULL, G_TYPE_INVALID);
+	if (!call)
+		nm_log_warn (LOGD_BT, "failed to request default Bluetooth adapter.");
+}
+
+static void
+bluez_connect (NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	DBusGConnection *connection;
+
+	g_return_if_fail (priv->proxy == NULL);
+
+	connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	if (!connection)
+		return;
+
+	priv->proxy = dbus_g_proxy_new_for_name (connection,
+	                                         BLUEZ_SERVICE,
+	                                         BLUEZ_MANAGER_PATH,
+	                                         BLUEZ_MANAGER_INTERFACE);
+
+	dbus_g_proxy_add_signal (priv->proxy, "AdapterRemoved",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "AdapterRemoved",
+	                             G_CALLBACK (adapter_removed), self, NULL);
+
+	dbus_g_proxy_add_signal (priv->proxy, "DefaultAdapterChanged",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "DefaultAdapterChanged",
+	                             G_CALLBACK (default_adapter_changed), self, NULL);
+
+	query_default_adapter (self);
+}
+
+static void
+remove_all_devices (NMBluezManager *self, gboolean do_signal)
+{
+	/* FIXME: do something */
+}
+
+static void
+name_owner_changed_cb (NMDBusManager *dbus_mgr,
+                       const char *name,
+                       const char *old_owner,
+                       const char *new_owner,
+                       gpointer user_data)
+{
+	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
+	gboolean old_owner_good = (old_owner && strlen (old_owner));
+	gboolean new_owner_good = (new_owner && strlen (new_owner));
+
+	/* Can't handle the signal if its not from the supplicant service */
+	if (strcmp (BLUEZ_SERVICE, name))
+		return;
+
+	if (old_owner_good && !new_owner_good)
+		remove_all_devices (self, TRUE);
+}
+
+static void
+bluez_cleanup (NMBluezManager *self, gboolean do_signal)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	if (priv->proxy) {
+		g_object_unref (priv->proxy);
+		priv->proxy = NULL;
+	}
+
+	remove_all_devices (self, do_signal);
+}
+
+static void
+dbus_connection_changed_cb (NMDBusManager *dbus_mgr,
+                            DBusGConnection *connection,
+                            gpointer user_data)
+{
+	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
+
+	if (!connection)
+		bluez_cleanup (self, TRUE);
+	else
+		bluez_connect (self);
+}
+
+
+NMBluezManager *
+nm_bluez_manager_new (void)
+{
+	return NM_BLUEZ_MANAGER (g_object_new (NM_TYPE_BLUEZ_MANAGER, NULL));
+}
+
+NMBluezManager *
+nm_bluez_manager_get (void)
+{
+	static NMBluezManager *singleton = NULL;
+
+	if (!singleton)
+		singleton = nm_bluez_manager_new ();
+	else
+		g_object_ref (singleton);
+
+	g_assert (singleton);
+	return singleton;
+}
+
+static void
+nm_bluez_manager_init (NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+	g_assert (priv->dbus_mgr);
+
+	g_signal_connect (priv->dbus_mgr,
+	                  "name-owner-changed",
+	                  G_CALLBACK (name_owner_changed_cb),
+	                  self);
+
+	g_signal_connect (priv->dbus_mgr,
+	                  "dbus-connection-changed",
+	                  G_CALLBACK (dbus_connection_changed_cb),
+	                  self);
+
+	bluez_connect (self);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMBluezManager *self = NM_BLUEZ_MANAGER (object);
+
+	bluez_cleanup (self, FALSE);
+
+	G_OBJECT_CLASS (nm_bluez_manager_parent_class)->finalize (object);
+}
+
+static void
+nm_bluez_manager_class_init (NMBluezManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMBluezManagerPrivate));
+
+	/* virtual methods */
+	object_class->finalize = finalize;
+
+	/* Signals */
+	signals[BDADDR_ADDED] =
+		g_signal_new ("bdaddr-added",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMBluezManagerClass, bdaddr_added),
+		              NULL, NULL,
+		              _nm_marshal_VOID__STRING_STRING_STRING_UINT,
+		              G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
+
+	signals[BDADDR_REMOVED] =
+		g_signal_new ("bdaddr-removed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMBluezManagerClass, bdaddr_removed),
+		              NULL, NULL,
+		              _nm_marshal_VOID__STRING_STRING,
+		              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+}
+
diff --git a/src/bluez-manager/nm-bluez-manager.h b/src/bluez-manager/nm-bluez-manager.h
new file mode 100644
index 00000000..9cfab1b7
--- /dev/null
+++ b/src/bluez-manager/nm-bluez-manager.h
@@ -0,0 +1,63 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_BLUEZ_MANAGER_H
+#define NM_BLUEZ_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_BLUEZ_MANAGER            (nm_bluez_manager_get_type ())
+#define NM_BLUEZ_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager))
+#define NM_BLUEZ_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+#define NM_IS_BLUEZ_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER))
+#define NM_IS_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER))
+#define NM_BLUEZ_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+
+typedef struct {
+	GObject parent;
+} NMBluezManager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Virtual functions */
+	void (*bdaddr_added) (NMBluezManager *manager,
+	                      const char *bdaddr,
+	                      const char *name,
+	                      const char *object_path,
+	                      guint uuids);
+
+	void (*bdaddr_removed) (NMBluezManager *manager,
+	                        const char *bdaddr,
+	                        const char *object_path);
+} NMBluezManagerClass;
+
+GType nm_bluez_manager_get_type (void);
+
+NMBluezManager *nm_bluez_manager_new (void);
+NMBluezManager *nm_bluez_manager_get (void);
+void nm_bluez_manager_query_devices (NMBluezManager *manager);
+
+#endif /* NM_BLUEZ_MANAGER_H */
+
diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am
new file mode 100644
index 00000000..f75e6b3d
--- /dev/null
+++ b/src/dhcp-manager/Makefile.am
@@ -0,0 +1,39 @@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/marshallers \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/named-manager
+
+noinst_LTLIBRARIES = libdhcp-manager.la
+
+libdhcp_manager_la_SOURCES = \
+	nm-dhcp-client.c \
+	nm-dhcp-client.h \
+	nm-dhcp-manager.c \
+	nm-dhcp-manager.h \
+	nm-dhcp-dhclient.h \
+	nm-dhcp-dhclient.c \
+	nm-dhcp-dhcpcd.h \
+	nm-dhcp-dhcpcd.c
+
+libdhcp_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
+	-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
+	-DDHCPCD_PATH=\"$(DHCPCD_PATH)\"
+
+libdhcp_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
diff --git a/src/dhcp-manager/Makefile.in b/src/dhcp-manager/Makefile.in
new file mode 100644
index 00000000..07b9be9d
--- /dev/null
+++ b/src/dhcp-manager/Makefile.in
@@ -0,0 +1,610 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/dhcp-manager
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libdhcp_manager_la_DEPENDENCIES =  \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libdhcp_manager_la_OBJECTS = libdhcp_manager_la-nm-dhcp-client.lo \
+	libdhcp_manager_la-nm-dhcp-manager.lo \
+	libdhcp_manager_la-nm-dhcp-dhclient.lo \
+	libdhcp_manager_la-nm-dhcp-dhcpcd.lo
+libdhcp_manager_la_OBJECTS = $(am_libdhcp_manager_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libdhcp_manager_la_SOURCES)
+DIST_SOURCES = $(libdhcp_manager_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/marshallers \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/named-manager
+
+noinst_LTLIBRARIES = libdhcp-manager.la
+libdhcp_manager_la_SOURCES = \
+	nm-dhcp-client.c \
+	nm-dhcp-client.h \
+	nm-dhcp-manager.c \
+	nm-dhcp-manager.h \
+	nm-dhcp-dhclient.h \
+	nm-dhcp-dhclient.c \
+	nm-dhcp-dhcpcd.h \
+	nm-dhcp-dhcpcd.c
+
+libdhcp_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DBINDIR=\"$(bindir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
+	-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
+	-DDHCPCD_PATH=\"$(DHCPCD_PATH)\"
+
+libdhcp_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+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/dhcp-manager/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/dhcp-manager/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libdhcp-manager.la: $(libdhcp_manager_la_OBJECTS) $(libdhcp_manager_la_DEPENDENCIES) 
+	$(LINK)  $(libdhcp_manager_la_OBJECTS) $(libdhcp_manager_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdhcp_manager_la-nm-dhcp-client.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhclient.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhcpcd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdhcp_manager_la-nm-dhcp-manager.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libdhcp_manager_la-nm-dhcp-client.lo: nm-dhcp-client.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdhcp_manager_la-nm-dhcp-client.lo -MD -MP -MF $(DEPDIR)/libdhcp_manager_la-nm-dhcp-client.Tpo -c -o libdhcp_manager_la-nm-dhcp-client.lo `test -f 'nm-dhcp-client.c' || echo '$(srcdir)/'`nm-dhcp-client.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libdhcp_manager_la-nm-dhcp-client.Tpo $(DEPDIR)/libdhcp_manager_la-nm-dhcp-client.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dhcp-client.c' object='libdhcp_manager_la-nm-dhcp-client.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdhcp_manager_la-nm-dhcp-client.lo `test -f 'nm-dhcp-client.c' || echo '$(srcdir)/'`nm-dhcp-client.c
+
+libdhcp_manager_la-nm-dhcp-manager.lo: nm-dhcp-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdhcp_manager_la-nm-dhcp-manager.lo -MD -MP -MF $(DEPDIR)/libdhcp_manager_la-nm-dhcp-manager.Tpo -c -o libdhcp_manager_la-nm-dhcp-manager.lo `test -f 'nm-dhcp-manager.c' || echo '$(srcdir)/'`nm-dhcp-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libdhcp_manager_la-nm-dhcp-manager.Tpo $(DEPDIR)/libdhcp_manager_la-nm-dhcp-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dhcp-manager.c' object='libdhcp_manager_la-nm-dhcp-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdhcp_manager_la-nm-dhcp-manager.lo `test -f 'nm-dhcp-manager.c' || echo '$(srcdir)/'`nm-dhcp-manager.c
+
+libdhcp_manager_la-nm-dhcp-dhclient.lo: nm-dhcp-dhclient.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdhcp_manager_la-nm-dhcp-dhclient.lo -MD -MP -MF $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhclient.Tpo -c -o libdhcp_manager_la-nm-dhcp-dhclient.lo `test -f 'nm-dhcp-dhclient.c' || echo '$(srcdir)/'`nm-dhcp-dhclient.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhclient.Tpo $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhclient.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dhcp-dhclient.c' object='libdhcp_manager_la-nm-dhcp-dhclient.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdhcp_manager_la-nm-dhcp-dhclient.lo `test -f 'nm-dhcp-dhclient.c' || echo '$(srcdir)/'`nm-dhcp-dhclient.c
+
+libdhcp_manager_la-nm-dhcp-dhcpcd.lo: nm-dhcp-dhcpcd.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdhcp_manager_la-nm-dhcp-dhcpcd.lo -MD -MP -MF $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhcpcd.Tpo -c -o libdhcp_manager_la-nm-dhcp-dhcpcd.lo `test -f 'nm-dhcp-dhcpcd.c' || echo '$(srcdir)/'`nm-dhcp-dhcpcd.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhcpcd.Tpo $(DEPDIR)/libdhcp_manager_la-nm-dhcp-dhcpcd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dhcp-dhcpcd.c' object='libdhcp_manager_la-nm-dhcp-dhcpcd.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdhcp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdhcp_manager_la-nm-dhcp-dhcpcd.lo `test -f 'nm-dhcp-dhcpcd.c' || echo '$(srcdir)/'`nm-dhcp-dhcpcd.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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-noinstLTLIBRARIES \
+	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: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 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/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
new file mode 100644
index 00000000..31a6f83c
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -0,0 +1,1243 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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) 2005 - 2010 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-dhcp-client.h"
+
+typedef struct {
+	char *       iface;
+	gboolean     ipv6;
+	char *       uuid;
+	guint32      timeout;
+
+	guchar       state;
+	GPid         pid;
+	gboolean     dead;
+	guint        timeout_id;
+	guint        watch_id;
+	guint32      remove_id;
+	GHashTable * options;
+	gboolean     info_only;
+
+} NMDHCPClientPrivate;
+
+#define NM_DHCP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_CLIENT, NMDHCPClientPrivate))
+
+G_DEFINE_TYPE_EXTENDED (NMDHCPClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
+
+enum {
+	STATE_CHANGED,
+	TIMEOUT,
+	REMOVE,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_0,
+	PROP_IFACE,
+	PROP_IPV6,
+	PROP_UUID,
+	PROP_TIMEOUT,
+	LAST_PROP
+};
+
+/********************************************/
+
+GPid
+nm_dhcp_client_get_pid (NMDHCPClient *self)
+{
+	g_return_val_if_fail (self != NULL, -1);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->pid;
+}
+
+const char *
+nm_dhcp_client_get_iface (NMDHCPClient *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface;
+}
+
+gboolean
+nm_dhcp_client_get_ipv6 (NMDHCPClient *self)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->ipv6;
+}
+
+const char *
+nm_dhcp_client_get_uuid (NMDHCPClient *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid;
+}
+
+/********************************************/
+
+static void
+timeout_cleanup (NMDHCPClient *self)
+{
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	if (priv->timeout_id) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+}
+
+static void
+watch_cleanup (NMDHCPClient *self)
+{
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	if (priv->watch_id) {
+		g_source_remove (priv->watch_id);
+		priv->watch_id = 0;
+	}
+}
+
+static void
+stop_process (GPid pid, const char *iface)
+{
+	int i = 15; /* 3 seconds */
+
+	g_return_if_fail (pid > 0);
+
+	/* Tell it to quit; maybe it wants to send out a RELEASE message */
+	kill (pid, SIGTERM);
+
+	while (i-- > 0) {
+		gint child_status;
+		int ret;
+
+		ret = waitpid (pid, &child_status, WNOHANG);
+		if (ret > 0)
+			break;
+
+		if (ret == -1) {
+			/* Child already exited */
+			if (errno == ECHILD)
+				break;
+			/* Took too long; shoot it in the head */
+			i = 0;
+			break;
+		}
+		g_usleep (G_USEC_PER_SEC / 5);
+	}
+
+	if (i <= 0) {
+		if (iface) {
+			nm_log_warn (LOGD_DHCP, "(%s): DHCP client pid %d didn't exit, will kill it.",
+			             iface, pid);
+		}
+		kill (pid, SIGKILL);
+
+		nm_log_dbg (LOGD_DHCP, "waiting for DHCP client pid %d to exit", pid);
+		waitpid (pid, NULL, 0);
+		nm_log_dbg (LOGD_DHCP, "DHCP client pid %d cleaned up", pid);
+	}
+}
+
+static void
+real_stop (NMDHCPClient *self)
+{
+	NMDHCPClientPrivate *priv;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	g_return_if_fail (priv->pid > 0);
+
+	/* Clean up the watch handler since we're explicitly killing the daemon */
+	watch_cleanup (self);
+
+	stop_process (priv->pid, priv->iface);
+
+	priv->info_only = FALSE;
+}
+
+static gboolean
+daemon_timeout (gpointer user_data)
+{
+	NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	if (priv->ipv6) {
+		nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 request timed out.", priv->iface);
+	} else {
+		nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 request timed out.", priv->iface);
+	}
+	g_signal_emit (G_OBJECT (self), signals[TIMEOUT], 0);
+	return FALSE;
+}
+
+static gboolean
+signal_remove (gpointer user_data)
+{
+	NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
+
+	NM_DHCP_CLIENT_GET_PRIVATE (self)->remove_id = 0;
+	g_signal_emit (G_OBJECT (self), signals[REMOVE], 0);
+	return FALSE;
+}
+
+static void
+dhcp_client_set_state (NMDHCPClient *self,
+                       NMDHCPState state,
+                       gboolean emit_state,
+                       gboolean remove_now)
+{
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	priv->state = state;
+
+	if (emit_state)
+		g_signal_emit (G_OBJECT (self), signals[STATE_CHANGED], 0, priv->state);
+
+	if (state == DHC_END || state == DHC_ABEND) {
+		/* Start the remove signal timer */
+		if (remove_now) {
+			g_signal_emit (G_OBJECT (self), signals[REMOVE], 0);
+		} else {
+			if (!priv->remove_id)
+				priv->remove_id = g_timeout_add_seconds (5, signal_remove, self);
+		}
+	}
+}
+
+static void
+daemon_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+	NMDHCPClient *self = NM_DHCP_CLIENT (user_data);
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	NMDHCPState new_state;
+
+	if (priv->ipv6) {
+		nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 client pid %d exited with status %d",
+		             priv->iface, pid,
+		             WIFEXITED (status) ? WEXITSTATUS (status) : -1);
+	} else {
+		nm_log_info (LOGD_DHCP6, "(%s): DHCPv4 client pid %d exited with status %d",
+		             priv->iface, pid,
+		             WIFEXITED (status) ? WEXITSTATUS (status) : -1);
+	}
+
+	if (!WIFEXITED (status)) {
+		new_state = DHC_ABEND;
+		nm_log_warn (LOGD_DHCP, "DHCP client died abnormally");
+	} else
+		new_state = DHC_END;
+
+	watch_cleanup (self);
+	timeout_cleanup (self);
+	priv->dead = TRUE;
+
+	dhcp_client_set_state (self, new_state, TRUE, FALSE);
+}
+
+static void
+start_monitor (NMDHCPClient *self)
+{
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->pid > 0);
+
+	/* Set up a timeout on the transaction to kill it after the timeout */
+	priv->timeout_id = g_timeout_add_seconds (priv->timeout,
+	                                          daemon_timeout,
+	                                          self);
+	priv->watch_id = g_child_watch_add (priv->pid,
+	                                    (GChildWatchFunc) daemon_watch_cb,
+	                                    self);
+}
+
+gboolean
+nm_dhcp_client_start_ip4 (NMDHCPClient *self,
+                          NMSettingIP4Config *s_ip4,
+                          guint8 *dhcp_anycast_addr)
+{
+	NMDHCPClientPrivate *priv;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	g_return_val_if_fail (priv->pid == -1, FALSE);
+	g_return_val_if_fail (priv->ipv6 == FALSE, FALSE);
+	g_return_val_if_fail (priv->uuid != NULL, FALSE);
+
+	nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)",
+	             priv->iface, priv->timeout);
+
+	priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, s_ip4, dhcp_anycast_addr);
+	if (priv->pid)
+		start_monitor (self);
+
+	return priv->pid ? TRUE : FALSE;
+}
+
+gboolean
+nm_dhcp_client_start_ip6 (NMDHCPClient *self,
+                          NMSettingIP6Config *s_ip6,
+                          guint8 *dhcp_anycast_addr,
+                          gboolean info_only)
+{
+	NMDHCPClientPrivate *priv;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	g_return_val_if_fail (priv->pid == -1, FALSE);
+	g_return_val_if_fail (priv->ipv6 == TRUE, FALSE);
+	g_return_val_if_fail (priv->uuid != NULL, FALSE);
+
+	priv->info_only = info_only;
+
+	nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)",
+	             priv->iface, priv->timeout);
+
+	priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr, info_only);
+	if (priv->pid > 0)
+		start_monitor (self);
+
+	return priv->pid ? TRUE : FALSE;
+}
+
+void
+nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
+{
+	char *pid_contents = NULL, *proc_contents = NULL, *proc_path = NULL;
+	long int tmp;
+
+	/* Check for an existing instance and stop it */
+	if (!g_file_get_contents (pid_file, &pid_contents, NULL, NULL))
+		return;
+
+	errno = 0;
+	tmp = strtol (pid_contents, NULL, 10);
+	if ((errno == 0) && (tmp > 1)) {
+		const char *exe;
+
+		/* Ensure the process is a DHCP client */
+		proc_path = g_strdup_printf ("/proc/%ld/cmdline", tmp);
+		if (g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) {
+			exe = strrchr (proc_contents, '/');
+			if (exe)
+				exe++;
+			else
+				exe = proc_contents;
+
+			if (!strcmp (exe, binary_name))
+				stop_process ((GPid) tmp, NULL);
+		}
+	}
+
+	remove (pid_file);
+	g_free (proc_path);
+	g_free (pid_contents);
+	g_free (proc_contents);
+}
+
+void
+nm_dhcp_client_stop (NMDHCPClient *self)
+{
+	NMDHCPClientPrivate *priv;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	/* Kill the DHCP client */
+	if (!priv->dead) {
+		NM_DHCP_CLIENT_GET_CLASS (self)->stop (self);
+		priv->dead = TRUE;
+
+		nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction, DHCP client pid %d",
+		             priv->iface, priv->pid);
+	}
+
+	/* And clean stuff up */
+
+	priv->pid = -1;
+	dhcp_client_set_state (self, DHC_END, FALSE, TRUE);
+
+	g_hash_table_remove_all (priv->options);
+
+	timeout_cleanup (self);
+	watch_cleanup (self);
+}
+
+/********************************************/
+
+static gboolean
+state_is_bound (guint32 state)
+{
+	if (   (state == DHC_BOUND4)
+	    || (state == DHC_BOUND6)
+	    || (state == DHC_RENEW4)
+	    || (state == DHC_RENEW6)
+	    || (state == DHC_REBOOT)
+	    || (state == DHC_REBIND4)
+	    || (state == DHC_REBIND6)
+	    || (state == DHC_IPV4LL))
+		return TRUE;
+
+	return FALSE;
+}
+
+typedef struct {
+	NMDHCPState state;
+	const char *name;
+} DhcState;
+
+#define STATE_TABLE_SIZE (sizeof (state_table) / sizeof (state_table[0]))
+
+static DhcState state_table[] = {
+	{ DHC_NBI,     "nbi" },
+	{ DHC_PREINIT, "preinit" },
+	{ DHC_PREINIT6,"preinit6" },
+	{ DHC_BOUND4,  "bound" },
+	{ DHC_BOUND6,  "bound6" },
+	{ DHC_IPV4LL,  "ipv4ll" },
+	{ DHC_RENEW4,  "renew" },
+	{ DHC_RENEW6,  "renew6" },
+	{ DHC_REBOOT,  "reboot" },
+	{ DHC_REBIND4, "rebind" },
+	{ DHC_REBIND6, "rebind6" },
+	{ DHC_STOP,    "stop" },
+	{ DHC_STOP6,   "stop6" },
+	{ DHC_MEDIUM,  "medium" },
+	{ DHC_TIMEOUT, "timeout" },
+	{ DHC_FAIL,    "fail" },
+	{ DHC_EXPIRE,  "expire" },
+	{ DHC_EXPIRE6, "expire6" },
+	{ DHC_RELEASE, "release" },
+	{ DHC_RELEASE6,"release6" },
+	{ DHC_START,   "start" },
+	{ DHC_ABEND,   "abend" },
+	{ DHC_END,     "end" },
+	{ DHC_DEPREF6, "depref6" },
+};
+
+static inline const char *
+state_to_string (guint32 state)
+{
+	int i;
+
+	for (i = 0; i < STATE_TABLE_SIZE; i++) {
+		if (state == state_table[i].state)
+			return state_table[i].name;
+	}
+
+	return NULL;
+}
+
+static inline NMDHCPState
+string_to_state (const char *name)
+{
+	int i;
+
+	for (i = 0; i < STATE_TABLE_SIZE; i++) {
+		if (!strcasecmp (name, state_table[i].name))
+			return state_table[i].state;
+	}
+
+	return 255;
+}
+
+static char *
+garray_to_string (GArray *array, const char *key)
+{
+	GString *str;
+	int i;
+	unsigned char c;
+	char *converted = NULL;
+
+	g_return_val_if_fail (array != NULL, NULL);
+
+	/* Since the DHCP options come through environment variables, they should
+	 * already be UTF-8 safe, but just make sure.
+	 */
+	str = g_string_sized_new (array->len);
+	for (i = 0; i < array->len; i++) {
+		c = array->data[i];
+
+		/* Convert NULLs to spaces and non-ASCII characters to ? */
+		if (c == '\0')
+			c = ' ';
+		else if (c > 127)
+			c = '?';
+		str = g_string_append_c (str, c);
+	}
+	str = g_string_append_c (str, '\0');
+
+	converted = str->str;
+	if (!g_utf8_validate (converted, -1, NULL))
+		nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
+	g_string_free (str, FALSE);
+	return converted;
+}
+
+static void
+copy_option (gpointer key,
+             gpointer value,
+             gpointer user_data)
+{
+	GHashTable *hash = user_data;
+	const char *str_key = (const char *) key;
+	char *str_value = NULL;
+
+	if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
+		nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
+		             "DBUS_TYPE_G_UCHAR_ARRAY",
+		             str_key);
+		return;
+	}
+
+	str_value = garray_to_string ((GArray *) g_value_get_boxed (value), str_key);
+	if (str_value)
+		g_hash_table_insert (hash, g_strdup (str_key), str_value);
+}
+
+void
+nm_dhcp_client_new_options (NMDHCPClient *self,
+                            GHashTable *options,
+                            const char *reason)
+{
+	NMDHCPClientPrivate *priv;
+	guint32 old_state;
+	guint32 new_state;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
+	g_return_if_fail (options != NULL);
+	g_return_if_fail (reason != NULL);
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	old_state = priv->state;
+	new_state = string_to_state (reason);
+
+	/* Clear old and save new DHCP options */
+	g_hash_table_remove_all (priv->options);
+	g_hash_table_foreach (options, copy_option, priv->options);
+
+	if (old_state == new_state)
+		return;
+
+	/* Handle changed device state */
+	if (state_is_bound (new_state)) {
+		/* Cancel the timeout if the DHCP client is now bound */
+		timeout_cleanup (self);
+	}
+
+	if (priv->ipv6) {
+		nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 state changed %s -> %s",
+		            priv->iface,
+		            state_to_string (old_state),
+		            state_to_string (new_state));
+	} else {
+		nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 state changed %s -> %s",
+		            priv->iface,
+		            state_to_string (old_state),
+		            state_to_string (new_state));
+	}
+
+	dhcp_client_set_state (self, new_state, TRUE, FALSE);
+}
+
+#define NEW_TAG "new_"
+#define OLD_TAG "old_"
+
+gboolean
+nm_dhcp_client_foreach_option (NMDHCPClient *self,
+                               GHFunc func,
+                               gpointer user_data)
+{
+	NMDHCPClientPrivate *priv;
+	GHashTableIter iter;
+	gpointer iterkey, itervalue;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+	g_return_val_if_fail (func != NULL, FALSE);
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	if (!state_is_bound (priv->state)) {
+		if (priv->ipv6) {
+			nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface);
+		} else {
+			nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface);
+		}
+	}
+
+	g_hash_table_iter_init (&iter, priv->options);
+	while (g_hash_table_iter_next (&iter, &iterkey, &itervalue)) {
+		const char *key = iterkey, *value = itervalue;
+		const char **p;
+		static const char *filter_options[] = {
+			"interface", "pid", "reason", "dhcp_message_type", NULL
+		};
+		gboolean ignore = FALSE;
+
+		/* Filter out stuff that's not actually new DHCP options */
+		for (p = filter_options; *p; p++) {
+			if (!strcmp (*p, key) || !strncmp (key, OLD_TAG, strlen (OLD_TAG))) {
+				ignore = TRUE;
+				break;
+			}
+		}
+
+		if (!ignore) {
+			const char *tmp_key = value;
+
+			/* Remove the "new_" prefix that dhclient passes back */
+			if (!strncmp (key, NEW_TAG, strlen (NEW_TAG)))
+				tmp_key = key + strlen (NEW_TAG);
+
+			func ((gpointer) tmp_key, (gpointer) value, user_data);
+		}
+	}
+	return TRUE;
+}
+
+/********************************************/
+
+static void
+process_classful_routes (GHashTable *options, NMIP4Config *ip4_config)
+{
+	const char *str;
+	char **searches, **s;
+
+	str = g_hash_table_lookup (options, "new_static_routes");
+	if (!str)
+		return;
+
+	searches = g_strsplit (str, " ", 0);
+	if ((g_strv_length (searches) % 2)) {
+		nm_log_info (LOGD_DHCP, "  static routes provided, but invalid");
+		goto out;
+	}
+
+	for (s = searches; *s; s += 2) {
+		NMIP4Route *route;
+		struct in_addr rt_addr;
+		struct in_addr rt_route;
+
+		if (inet_pton (AF_INET, *s, &rt_addr) <= 0) {
+			nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route address: '%s'", *s);
+			continue;
+		}
+		if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) {
+			nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route gateway: '%s'", *(s + 1));
+			continue;
+		}
+
+		// FIXME: ensure the IP addresse and route are sane
+
+		route = nm_ip4_route_new ();
+		nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr);
+		nm_ip4_route_set_prefix (route, 32); /* 255.255.255.255 */
+		nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr);
+
+		nm_ip4_config_take_route (ip4_config, route);
+		nm_log_info (LOGD_DHCP, "  static route %s gw %s", *s, *(s + 1));
+	}
+
+out:
+	g_strfreev (searches);
+}
+
+static void
+process_domain_search (const char *str, GFunc add_func, gpointer user_data)
+{
+	char **searches, **s;
+	char *unescaped, *p;
+	int i;
+
+	g_return_if_fail (str != NULL);
+	g_return_if_fail (add_func != NULL);
+
+	p = unescaped = g_strdup (str);
+	do {
+		p = strstr (p, "\\032");
+		if (!p)
+			break;
+
+		/* Clear the escaped space with real spaces */
+		for (i = 0; i < 4; i++)
+			*p++ = ' ';
+	} while (*p++);
+
+	if (strchr (unescaped, '\\')) {
+		nm_log_warn (LOGD_DHCP, "  invalid domain search: '%s'", unescaped);
+		goto out;
+	}
+
+	searches = g_strsplit (unescaped, " ", 0);
+	for (s = searches; *s; s++) {
+		if (strlen (*s)) {
+			nm_log_info (LOGD_DHCP, "  domain search '%s'", *s);
+			add_func (*s, user_data);
+		}
+	}
+	g_strfreev (searches);
+
+out:
+	g_free (unescaped);
+}
+
+static void
+ip4_add_domain_search (gpointer data, gpointer user_data)
+{
+	nm_ip4_config_add_search (NM_IP4_CONFIG (user_data), (const char *) data);
+}
+
+/* Given a table of DHCP options from the client, convert into an IP4Config */
+static NMIP4Config *
+ip4_options_to_config (NMDHCPClient *self)
+{
+	NMDHCPClientPrivate *priv;
+	NMIP4Config *ip4_config = NULL;
+	struct in_addr tmp_addr;
+	NMIP4Address *addr = NULL;
+	char *str = NULL;
+	guint32 gwaddr = 0, prefix = 0;
+	gboolean have_classless = FALSE;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	g_return_val_if_fail (priv->options != NULL, NULL);
+
+	ip4_config = nm_ip4_config_new ();
+	if (!ip4_config) {
+		nm_log_warn (LOGD_DHCP4, "(%s): couldn't allocate memory for an IP4Config!", priv->iface);
+		return NULL;
+	}
+
+	addr = nm_ip4_address_new ();
+	if (!addr) {
+		nm_log_warn (LOGD_DHCP4, "(%s): couldn't allocate memory for an IP4 Address!", priv->iface);
+		goto error;
+	}
+
+	str = g_hash_table_lookup (priv->options, "new_ip_address");
+	if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
+		nm_ip4_address_set_address (addr, tmp_addr.s_addr);
+		nm_log_info (LOGD_DHCP4, "  address %s", str);
+	} else
+		goto error;
+
+	str = g_hash_table_lookup (priv->options, "new_subnet_mask");
+	if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
+		prefix = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr);
+		nm_log_info (LOGD_DHCP4, "  prefix %d (%s)", prefix, str);
+	} else {
+		/* Get default netmask for the IP according to appropriate class. */
+		prefix = nm_utils_ip4_get_default_prefix (nm_ip4_address_get_address (addr));
+		nm_log_info (LOGD_DHCP4, "  prefix %d (default)", prefix);
+	}
+	nm_ip4_address_set_prefix (addr, prefix);
+
+	/* Routes: if the server returns classless static routes, we MUST ignore
+	 * the 'static_routes' option.
+	 */
+	if (NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes) {
+		have_classless = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes (self,
+		                                                                                priv->options,
+		                                                                                ip4_config,
+		                                                                                &gwaddr);
+	}
+
+	if (!have_classless) {
+		gwaddr = 0;  /* Ensure client code doesn't lie */
+		process_classful_routes (priv->options, ip4_config);
+	}
+
+	if (gwaddr) {
+		char buf[INET_ADDRSTRLEN + 1];
+
+		inet_ntop (AF_INET, &gwaddr, buf, sizeof (buf));
+		nm_log_info (LOGD_DHCP4, "  gateway %s", buf);
+		nm_ip4_address_set_gateway (addr, gwaddr);
+	} else {
+		/* If the gateway wasn't provided as a classless static route with a
+		 * subnet length of 0, try to find it using the old-style 'routers' option.
+		 */
+		str = g_hash_table_lookup (priv->options, "new_routers");
+		if (str) {
+			char **routers = g_strsplit (str, " ", 0);
+			char **s;
+
+			for (s = routers; *s; s++) {
+				/* FIXME: how to handle multiple routers? */
+				if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+					nm_ip4_address_set_gateway (addr, tmp_addr.s_addr);
+					nm_log_info (LOGD_DHCP4, "  gateway %s", *s);
+					break;
+				} else
+					nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s);
+			}
+			g_strfreev (routers);
+		}
+	}
+
+	nm_ip4_config_take_address (ip4_config, addr);
+	addr = NULL;
+
+	str = g_hash_table_lookup (priv->options, "new_host_name");
+	if (str)
+		nm_log_info (LOGD_DHCP4, "  hostname '%s'", str);
+
+	str = g_hash_table_lookup (priv->options, "new_domain_name_servers");
+	if (str) {
+		char **searches = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = searches; *s; s++) {
+			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+				nm_ip4_config_add_nameserver (ip4_config, tmp_addr.s_addr);
+				nm_log_info (LOGD_DHCP4, "  nameserver '%s'", *s);
+			} else
+				nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s);
+		}
+		g_strfreev (searches);
+	}
+
+	str = g_hash_table_lookup (priv->options, "new_domain_name");
+	if (str) {
+		char **domains = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = domains; *s; s++) {
+			nm_log_info (LOGD_DHCP4, "  domain name '%s'", *s);
+			nm_ip4_config_add_domain (ip4_config, *s);
+		}
+		g_strfreev (domains);
+	}
+
+	str = g_hash_table_lookup (priv->options, "new_domain_search");
+	if (str)
+		process_domain_search (str, ip4_add_domain_search, ip4_config);
+
+	str = g_hash_table_lookup (priv->options, "new_netbios_name_servers");
+	if (str) {
+		char **searches = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = searches; *s; s++) {
+			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+				nm_ip4_config_add_wins (ip4_config, tmp_addr.s_addr);
+				nm_log_info (LOGD_DHCP4, "  wins '%s'", *s);
+			} else
+				nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s);
+		}
+		g_strfreev (searches);
+	}
+
+	str = g_hash_table_lookup (priv->options, "new_interface_mtu");
+	if (str) {
+		int int_mtu;
+
+		errno = 0;
+		int_mtu = strtol (str, NULL, 10);
+		if ((errno == EINVAL) || (errno == ERANGE))
+			goto error;
+
+		if (int_mtu > 576)
+			nm_ip4_config_set_mtu (ip4_config, int_mtu);
+	}
+
+	str = g_hash_table_lookup (priv->options, "new_nis_domain");
+	if (str) {
+		nm_log_info (LOGD_DHCP4, "  NIS domain '%s'", str);
+		nm_ip4_config_set_nis_domain (ip4_config, str);
+	}
+
+	str = g_hash_table_lookup (priv->options, "new_nis_servers");
+	if (str) {
+		char **searches = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = searches; *s; s++) {
+			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+				nm_ip4_config_add_nis_server (ip4_config, tmp_addr.s_addr);
+				nm_log_info (LOGD_DHCP4, "  nis '%s'", *s);
+			} else
+				nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s);
+		}
+		g_strfreev (searches);
+	}
+
+	return ip4_config;
+
+error:
+	if (addr)
+		nm_ip4_address_unref (addr);
+	g_object_unref (ip4_config);
+	return NULL;
+}
+
+NMIP4Config *
+nm_dhcp_client_get_ip4_config (NMDHCPClient *self, gboolean test)
+{
+	NMDHCPClientPrivate *priv;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	if (test && !state_is_bound (priv->state)) {
+		nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface);
+		return NULL;
+	}
+
+	if (!g_hash_table_size (priv->options)) {
+		/* We never got a response from the DHCP client */
+		return NULL;
+	}
+
+	return ip4_options_to_config (self);
+}
+
+/********************************************/
+
+static void
+ip6_add_domain_search (gpointer data, gpointer user_data)
+{
+	nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data);
+}
+
+/* Given a table of DHCP options from the client, convert into an IP6Config */
+static NMIP6Config *
+ip6_options_to_config (NMDHCPClient *self)
+{
+	NMDHCPClientPrivate *priv;
+	NMIP6Config *ip6_config = NULL;
+	struct in6_addr tmp_addr;
+	NMIP6Address *addr = NULL;
+	char *str = NULL;
+	GHashTableIter iter;
+	gpointer key, value;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	g_return_val_if_fail (priv->options != NULL, NULL);
+
+	g_hash_table_iter_init (&iter, priv->options);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'",
+		            priv->iface, (const char *) key, (const char *) value);
+	}
+
+	ip6_config = nm_ip6_config_new ();
+	if (!ip6_config) {
+		nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6Config!", priv->iface);
+		return NULL;
+	}
+
+	addr = nm_ip6_address_new ();
+	if (!addr) {
+		nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6 Address!", priv->iface);
+		goto error;
+	}
+
+	str = g_hash_table_lookup (priv->options, "new_ip6_address");
+	if (str) {
+		if (!inet_pton (AF_INET6, str, &tmp_addr)) {
+			nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'",
+			             priv->iface);
+			goto error;
+		}
+
+		nm_ip6_address_set_address (addr, &tmp_addr);
+		nm_log_info (LOGD_DHCP6, "  address %s", str);
+	} else {
+		/* No address in managed mode is a hard error */
+		if (priv->info_only == FALSE)
+			goto error;
+
+		/* But "info-only" setups don't necessarily need an address */
+		nm_ip6_address_unref (addr);
+		addr = NULL;
+	}
+
+	/* Only care about prefix if we got an address */
+	if (addr) {
+		str = g_hash_table_lookup (priv->options, "new_ip6_prefixlen");
+		if (str) {
+			long unsigned int prefix;
+
+			errno = 0;
+			prefix = strtoul (str, NULL, 10);
+			if (errno != 0 || prefix > 128)
+				goto error;
+
+			nm_ip6_address_set_prefix (addr, (guint32) prefix);
+			nm_log_info (LOGD_DHCP6, "  prefix %lu", prefix);
+		}
+
+		nm_ip6_config_take_address (ip6_config, addr);
+		addr = NULL;
+	}
+
+	str = g_hash_table_lookup (priv->options, "new_host_name");
+	if (str)
+		nm_log_info (LOGD_DHCP6, "  hostname '%s'", str);
+
+	str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers");
+	if (str) {
+		char **searches = g_strsplit (str, " ", 0);
+		char **s;
+
+		for (s = searches; *s; s++) {
+			if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) {
+				nm_ip6_config_add_nameserver (ip6_config, &tmp_addr);
+				nm_log_info (LOGD_DHCP6, "  nameserver '%s'", *s);
+			} else
+				nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s);
+		}
+		g_strfreev (searches);
+	}
+
+	str = g_hash_table_lookup (priv->options, "new_dhcp6_domain_search");
+	if (str)
+		process_domain_search (str, ip6_add_domain_search, ip6_config);
+
+	return ip6_config;
+
+error:
+	if (addr)
+		nm_ip6_address_unref (addr);
+	g_object_unref (ip6_config);
+	return NULL;
+}
+
+NMIP6Config *
+nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test)
+{
+	NMDHCPClientPrivate *priv;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	if (test && !state_is_bound (priv->state)) {
+		nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface);
+		return NULL;
+	}
+
+	if (!g_hash_table_size (priv->options)) {
+		/* We never got a response from the DHCP client */
+		return NULL;
+	}
+
+	return ip6_options_to_config (self);
+}
+
+/********************************************/
+
+static void
+nm_dhcp_client_init (NMDHCPClient *self)
+{
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+	priv->pid = -1;
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_IFACE:
+		g_value_set_string (value, priv->iface);
+		break;
+	case PROP_IPV6:
+		g_value_set_boolean (value, priv->ipv6);
+		break;
+	case PROP_UUID:
+		g_value_set_string (value, priv->uuid);
+		break;
+	case PROP_TIMEOUT:
+		g_value_set_uint (value, priv->timeout);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (object);
+ 
+	switch (prop_id) {
+	case PROP_IFACE:
+		/* construct-only */
+		priv->iface = g_strdup (g_value_get_string (value));
+		break;
+	case PROP_IPV6:
+		/* construct-only */
+		priv->ipv6 = g_value_get_boolean (value);
+		break;
+	case PROP_UUID:
+		/* construct-only */
+		priv->uuid = g_value_dup_string (value);
+		break;
+	case PROP_TIMEOUT:
+		priv->timeout = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDHCPClient *self = NM_DHCP_CLIENT (object);
+	NMDHCPClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+	/* Stopping the client is left up to the controlling device
+	 * explicitly since we may want to quit NetworkManager but not terminate
+	 * the DHCP client.
+	 */
+
+	if (priv->remove_id)
+		g_source_remove (priv->remove_id);
+
+	g_hash_table_destroy (priv->options);
+	g_free (priv->iface);
+
+	G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object);
+}
+
+static void
+nm_dhcp_client_class_init (NMDHCPClientClass *client_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (client_class);
+
+	g_type_class_add_private (client_class, sizeof (NMDHCPClientPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+
+	client_class->stop = real_stop;
+
+	g_object_class_install_property
+		(object_class, PROP_IFACE,
+		 g_param_spec_string (NM_DHCP_CLIENT_INTERFACE,
+		                      "iface",
+		                      "Interface",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_IPV6,
+		 g_param_spec_boolean (NM_DHCP_CLIENT_IPV6,
+		                       "ipv6",
+		                       "IPv6",
+		                       FALSE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_UUID,
+		 g_param_spec_string (NM_DHCP_CLIENT_UUID,
+		                      "uuid",
+		                      "UUID",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_TIMEOUT,
+		 g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT,
+		                    "timeout",
+		                    "Timeout",
+		                    0, G_MAXUINT, 45,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/* signals */
+	signals[STATE_CHANGED] =
+		g_signal_new ("state-changed",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMDHCPClientClass, state_changed),
+					  NULL, NULL,
+					  g_cclosure_marshal_VOID__UINT,
+					  G_TYPE_NONE, 1, G_TYPE_UINT);
+
+	signals[TIMEOUT] =
+		g_signal_new ("timeout",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMDHCPClientClass, timeout),
+					  NULL, NULL,
+					  g_cclosure_marshal_VOID__VOID,
+					  G_TYPE_NONE, 0);
+
+	signals[REMOVE] =
+		g_signal_new ("remove",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMDHCPClientClass, remove),
+					  NULL, NULL,
+					  g_cclosure_marshal_VOID__VOID,
+					  G_TYPE_NONE, 0);
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h
new file mode 100644
index 00000000..92b2b8fe
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-client.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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) 2005 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP_CLIENT_H
+#define NM_DHCP_CLIENT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
+#include <nm-ip4-config.h>
+#include <nm-ip6-config.h>
+
+#define NM_TYPE_DHCP_CLIENT            (nm_dhcp_client_get_type ())
+#define NM_DHCP_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClient))
+#define NM_DHCP_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass))
+#define NM_IS_DHCP_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_CLIENT))
+#define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_CLIENT))
+#define NM_DHCP_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDHCPClientClass))
+
+#define NM_DHCP_CLIENT_INTERFACE "iface"
+#define NM_DHCP_CLIENT_IPV6      "ipv6"
+#define NM_DHCP_CLIENT_UUID      "uuid"
+#define NM_DHCP_CLIENT_TIMEOUT   "timeout"
+
+typedef enum {
+	DHC_NBI = 0,     /* no broadcast interfaces found */
+	DHC_PREINIT,     /* configuration started */
+	DHC_PREINIT6,    /* configuration started */
+	DHC_BOUND4,      /* IPv4 lease obtained */
+	DHC_BOUND6,      /* IPv6 lease obtained */
+	DHC_IPV4LL,      /* IPv4LL address obtained */
+	DHC_RENEW4,      /* IPv4 lease renewed */
+	DHC_RENEW6,      /* IPv6 lease renewed */
+	DHC_REBOOT,      /* have valid lease, but now obtained a different one */
+	DHC_REBIND4,     /* IPv4 new/different lease */
+	DHC_REBIND6,     /* IPv6 new/different lease */
+	DHC_DEPREF6,     /* IPv6 lease depreferred */
+	DHC_STOP,        /* remove old lease */
+	DHC_STOP6,       /* remove old lease */
+	DHC_MEDIUM,      /* media selection begun */
+	DHC_TIMEOUT,     /* timed out contacting DHCP server */
+	DHC_FAIL,        /* all attempts to contact server timed out, sleeping */
+	DHC_EXPIRE,      /* lease has expired, renewing */
+	DHC_EXPIRE6,     /* lease has expired, renewing */
+	DHC_RELEASE,     /* releasing lease */
+	DHC_RELEASE6,    /* releasing lease */
+	DHC_START,       /* sent when dhclient started OK */
+	DHC_ABEND,       /* dhclient exited abnormally */
+	DHC_END,         /* dhclient exited normally */
+	DHC_END_OPTIONS, /* last option in subscription sent */
+} NMDHCPState;
+
+typedef struct {
+	GObject parent;
+} NMDHCPClient;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Methods */
+
+	/* Given the options table, extract any classless routes, add them to
+	 * the IP4 config and return TRUE if any existed.  If a gateway was sent
+	 * as a classless route return that in out_gwaddr.
+	 */
+	gboolean (*ip4_process_classless_routes) (NMDHCPClient *self,
+	                                          GHashTable *options,
+	                                          NMIP4Config *ip4_config,
+	                                          guint32 *out_gwaddr);
+
+	GPid (*ip4_start)                        (NMDHCPClient *self,
+	                                          NMSettingIP4Config *s_ip4,
+	                                          guint8 *anycast_addr);
+
+	GPid (*ip6_start)                        (NMDHCPClient *self,
+	                                          NMSettingIP6Config *s_ip6,
+	                                          guint8 *anycast_addr,
+	                                          gboolean info_only);
+
+	void (*stop)                             (NMDHCPClient *self);
+
+	/* Signals */
+	void (*state_changed) (NMDHCPClient *self, NMDHCPState state);
+	void (*timeout)       (NMDHCPClient *self);
+	void (*remove)        (NMDHCPClient *self);
+} NMDHCPClientClass;
+
+GType nm_dhcp_client_get_type (void);
+
+GPid nm_dhcp_client_get_pid (NMDHCPClient *self);
+
+const char *nm_dhcp_client_get_iface (NMDHCPClient *self);
+
+gboolean nm_dhcp_client_get_ipv6 (NMDHCPClient *self);
+
+const char *nm_dhcp_client_get_uuid (NMDHCPClient *self);
+
+gboolean nm_dhcp_client_start_ip4 (NMDHCPClient *self,
+                                   NMSettingIP4Config *s_ip4,
+                                   guint8 *dhcp_anycast_addr);
+
+gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self,
+                                   NMSettingIP6Config *s_ip6,
+                                   guint8 *dhcp_anycast_addr,
+                                   gboolean info_only);
+
+void nm_dhcp_client_stop (NMDHCPClient *self);
+
+void nm_dhcp_client_new_options (NMDHCPClient *self,
+                                 GHashTable *options,
+                                 const char *reason);
+
+gboolean nm_dhcp_client_foreach_option (NMDHCPClient *self,
+                                        GHFunc func,
+                                        gpointer user_data);
+
+NMIP4Config *nm_dhcp_client_get_ip4_config   (NMDHCPClient *self, gboolean test);
+
+NMIP6Config *nm_dhcp_client_get_ip6_config   (NMDHCPClient *self, gboolean test);
+
+/* Backend helpers */
+void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name);
+
+#endif /* NM_DHCP_CLIENT_H */
+
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
new file mode 100644
index 00000000..bb5cd2e8
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -0,0 +1,786 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-dhcp-dhclient.c - dhclient specific hooks for 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) 2005 - 2010 Red Hat, Inc.
+ */
+
+#define _XOPEN_SOURCE
+#include <time.h>
+#undef _XOPEN_SOURCE
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+#include <config.h>
+
+#include "nm-dhcp-dhclient.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+
+G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
+
+#define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientPrivate))
+
+#if defined(TARGET_DEBIAN) || defined(TARGET_SUSE) || defined(TARGET_MANDRIVA)
+#define NM_DHCLIENT_LEASE_DIR           LOCALSTATEDIR "/lib/dhcp"
+#else
+#define NM_DHCLIENT_LEASE_DIR           LOCALSTATEDIR "/lib/dhclient"
+#endif
+
+#define ACTION_SCRIPT_PATH	LIBEXECDIR "/nm-dhcp-client.action"
+
+typedef struct {
+	const char *path;
+	char *conf_file;
+	char *lease_file;
+	char *pid_file;
+} NMDHCPDhclientPrivate;
+
+const char *
+nm_dhcp_dhclient_get_path (const char *try_first)
+{
+	static const char *dhclient_paths[] = {
+		"/sbin/dhclient",
+		"/usr/sbin/dhclient",
+		"/usr/pkg/sbin/dhclient",
+		"/usr/local/sbin/dhclient",
+		NULL
+	};
+	const char **path = dhclient_paths;
+
+	if (strlen (try_first) && g_file_test (try_first, G_FILE_TEST_EXISTS))
+		return try_first;
+
+	while (*path != NULL) {
+		if (g_file_test (*path, G_FILE_TEST_EXISTS))
+			break;
+		path++;
+	}
+
+	return *path;
+}
+
+static char *
+get_leasefile_for_iface (const char * iface, const char *uuid, gboolean ipv6)
+{
+	return g_strdup_printf ("%s/dhclient%s-%s-%s.lease",
+	                        NM_DHCLIENT_LEASE_DIR,
+	                        ipv6 ? "6" : "",
+	                        uuid,
+	                        iface);
+}
+
+static void
+add_lease_option (GHashTable *hash, char *line)
+{
+	char *spc;
+
+	spc = strchr (line, ' ');
+	if (!spc) {
+		nm_log_warn (LOGD_DHCP, "DHCP lease file line '%s' did not contain a space", line);
+		return;
+	}
+
+	/* If it's an 'option' line, split at second space */
+	if (g_str_has_prefix (line, "option ")) {
+		spc = strchr (spc + 1, ' ');
+		if (!spc) {
+			nm_log_warn (LOGD_DHCP, "DHCP lease file option line '%s' did not contain a second space",
+			             line);
+			return;
+		}
+	}
+
+	/* Split the line at the space */
+	*spc = '\0';
+	spc++;
+
+	/* Kill the ';' at the end of the line, if any */
+	if (*(spc + strlen (spc) - 1) == ';')
+		*(spc + strlen (spc) - 1) = '\0';
+
+	/* Treat 'interface' specially */
+	if (g_str_has_prefix (line, "interface")) {
+		if (*(spc) == '"')
+			spc++; /* Jump past the " */
+		if (*(spc + strlen (spc) - 1) == '"')
+			*(spc + strlen (spc) - 1) = '\0';  /* Kill trailing " */
+	}
+
+	g_hash_table_insert (hash, g_strdup (line), g_strdup (spc));
+}
+
+GSList *
+nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid)
+{
+	GSList *parsed = NULL, *iter, *leases = NULL;
+	char *contents = NULL;
+	char *leasefile;
+	char **line, **split = NULL;
+	GHashTable *hash = NULL;
+
+	leasefile = get_leasefile_for_iface (iface, uuid, FALSE);
+	if (!leasefile)
+		return NULL;
+
+	if (!g_file_test (leasefile, G_FILE_TEST_EXISTS))
+		goto out;
+
+	if (!g_file_get_contents (leasefile, &contents, NULL, NULL))
+		goto out;
+
+	split = g_strsplit_set (contents, "\n\r", -1);
+	g_free (contents);
+	if (!split)
+		goto out;
+
+	for (line = split; line && *line; line++) {
+		*line = g_strstrip (*line);
+
+		if (!strcmp (*line, "}")) {
+			/* Lease ends */
+			parsed = g_slist_append (parsed, hash);
+			hash = NULL;
+		} else if (!strcmp (*line, "lease {")) {
+			/* Beginning of a new lease */
+			if (hash) {
+				nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started "
+				             "without ending previous lease",
+				             leasefile);
+				g_hash_table_destroy (hash);
+			}
+
+			hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+		} else if (strlen (*line))
+			add_lease_option (hash, *line);
+	}
+	g_strfreev (split);
+
+	/* Check if the last lease in the file was properly ended */
+	if (hash) {
+		nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started "
+		             "without ending previous lease",
+		             leasefile);
+		g_hash_table_destroy (hash);
+		hash = NULL;
+	}
+
+	for (iter = parsed; iter; iter = g_slist_next (iter)) {
+		NMIP4Config *ip4;
+		NMIP4Address *addr;
+		const char *data;
+		struct in_addr tmp;
+		guint32 prefix;
+		struct tm expire;
+
+		hash = iter->data;
+
+		/* Make sure this lease is for the interface we want */
+		data = g_hash_table_lookup (hash, "interface");
+		if (!data || strcmp (data, iface))
+			continue;
+
+		data = g_hash_table_lookup (hash, "expire");
+		if (data) {
+			time_t now_tt;
+			struct tm *now;
+
+			/* Read lease expiration (in UTC) */
+			if (!strptime (data, "%w %Y/%m/%d %H:%M:%S", &expire)) {
+				nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file expire time '%s'",
+				             data);
+				continue;
+			}
+
+			now_tt = time (NULL);
+			now = gmtime(&now_tt);
+
+			/* Ignore this lease if it's already expired */
+			if (expire.tm_year < now->tm_year)
+				continue;
+			else if (expire.tm_year == now->tm_year) {
+				if (expire.tm_mon < now->tm_mon)
+					continue;
+				else if (expire.tm_mon == now->tm_mon) {
+					if (expire.tm_mday < now->tm_mday)
+						continue;
+					else if (expire.tm_mday == now->tm_mday) {
+						if (expire.tm_hour < now->tm_hour)
+							continue;
+						else if (expire.tm_hour == now->tm_hour) {
+							if (expire.tm_min < now->tm_min)
+								continue;
+							else if (expire.tm_min == now->tm_min) {
+								if (expire.tm_sec <= now->tm_sec)
+									continue;
+							}
+						}
+					}
+				}
+			}
+			/* If we get this far, the lease hasn't expired */
+		}
+
+		data = g_hash_table_lookup (hash, "fixed-address");
+		if (!data)
+			continue;
+
+		ip4 = nm_ip4_config_new ();
+		addr = nm_ip4_address_new ();
+
+		/* IP4 address */
+		if (!inet_pton (AF_INET, data, &tmp)) {
+			nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 address '%s'", data);
+			goto error;
+		}
+		nm_ip4_address_set_address (addr, tmp.s_addr);
+
+		/* Netmask */
+		data = g_hash_table_lookup (hash, "option subnet-mask");
+		if (data) {
+			if (!inet_pton (AF_INET, data, &tmp)) {
+				nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 subnet mask '%s'", data);
+				goto error;
+			}
+			prefix = nm_utils_ip4_netmask_to_prefix (tmp.s_addr);
+		} else {
+			/* Get default netmask for the IP according to appropriate class. */
+			prefix = nm_utils_ip4_get_default_prefix (nm_ip4_address_get_address (addr));
+		}
+		nm_ip4_address_set_prefix (addr, prefix);
+
+		/* Gateway */
+		data = g_hash_table_lookup (hash, "option routers");
+		if (data) {
+			if (!inet_pton (AF_INET, data, &tmp)) {
+				nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 gateway '%s'", data);
+				goto error;
+			}
+			nm_ip4_address_set_gateway (addr, tmp.s_addr);
+		}
+
+		nm_ip4_config_take_address (ip4, addr);
+		leases = g_slist_append (leases, ip4);
+		continue;
+
+	error:
+		nm_ip4_address_unref (addr);
+		g_object_unref (ip4);
+	}
+
+out:
+	g_slist_foreach (parsed, (GFunc) g_hash_table_destroy, NULL);
+	g_free (leasefile);
+	return leases;
+}
+
+
+#define DHCP_CLIENT_ID_TAG "send dhcp-client-identifier"
+#define DHCP_CLIENT_ID_FORMAT DHCP_CLIENT_ID_TAG " \"%s\"; # added by NetworkManager"
+#define DHCP_CLIENT_ID_FORMAT_OCTETS DHCP_CLIENT_ID_TAG " %s; # added by NetworkManager"
+
+#define DHCP_HOSTNAME_TAG "send host-name"
+#define DHCP_HOSTNAME_FORMAT DHCP_HOSTNAME_TAG " \"%s\"; # added by NetworkManager"
+
+static gboolean
+merge_dhclient_config (const char *iface,
+                       const char *conf_file,
+                       NMSettingIP4Config *s_ip4,
+                       guint8 *anycast_addr,
+                       const char *orig_path,
+                       GError **error)
+{
+	GString *new_contents;
+	char *orig_contents = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (conf_file != NULL, FALSE);
+
+	new_contents = g_string_new (_("# Created by NetworkManager\n"));
+
+	if (g_file_test (orig_path, G_FILE_TEST_EXISTS)) {
+		GError *read_error = NULL;
+
+		if (!g_file_get_contents (orig_path, &orig_contents, NULL, &read_error)) {
+			nm_log_warn (LOGD_DHCP, "(%s): error reading dhclient configuration %s: %s",
+			             iface, orig_path, read_error->message);
+			g_error_free (read_error);
+		}
+	}
+
+	/* Add existing options, if any, but ignore stuff NM will replace. */
+	if (orig_contents) {
+		char **lines = NULL, **line;
+
+		g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path);
+
+		lines = g_strsplit_set (orig_contents, "\n\r", 0);
+		for (line = lines; lines && *line; line++) {
+			gboolean ignore = FALSE;
+
+			if (!strlen (g_strstrip (*line)))
+				continue;
+
+			if (   s_ip4
+			    && nm_setting_ip4_config_get_dhcp_client_id (s_ip4)
+			    && !strncmp (*line, DHCP_CLIENT_ID_TAG, strlen (DHCP_CLIENT_ID_TAG)))
+				ignore = TRUE;
+
+			if (   s_ip4
+			    && nm_setting_ip4_config_get_dhcp_hostname (s_ip4)
+			    && !strncmp (*line, DHCP_HOSTNAME_TAG, strlen (DHCP_HOSTNAME_TAG)))
+				ignore = TRUE;
+
+			if (!ignore) {
+				g_string_append (new_contents, *line);
+				g_string_append_c (new_contents, '\n');
+			}
+		}
+
+		if (lines)
+			g_strfreev (lines);
+		g_free (orig_contents);
+	} else
+		g_string_append_c (new_contents, '\n');
+
+	/* Add NM options from connection */
+	if (s_ip4) {
+		const char *tmp;
+
+		tmp = nm_setting_ip4_config_get_dhcp_client_id (s_ip4);
+		if (tmp) {
+			gboolean is_octets = TRUE;
+			const char *p = tmp;
+
+			while (*p) {
+				if (!isxdigit (*p) && (*p != ':')) {
+					is_octets = FALSE;
+					break;
+				}
+				p++;
+			}
+
+			/* If the client ID is just hex digits and : then don't use quotes,
+			 * becuase dhclient expects either a quoted ASCII string, or a byte
+			 * array formated as hex octets separated by :
+			 */
+			if (is_octets)
+				g_string_append_printf (new_contents, DHCP_CLIENT_ID_FORMAT_OCTETS "\n", tmp);
+			else
+				g_string_append_printf (new_contents, DHCP_CLIENT_ID_FORMAT "\n", tmp);
+		}
+
+		tmp = nm_setting_ip4_config_get_dhcp_hostname (s_ip4);
+		if (tmp)
+			g_string_append_printf (new_contents, DHCP_HOSTNAME_FORMAT "\n", tmp);
+	}
+
+	if (anycast_addr) {
+		g_string_append_printf (new_contents, "interface \"%s\" {\n"
+		                        " initial-interval 1; \n"
+		                        " anycast-mac ethernet %02x:%02x:%02x:%02x:%02x:%02x;\n"
+		                        "}\n",
+		                        iface,
+		                        anycast_addr[0], anycast_addr[1],
+		                        anycast_addr[2], anycast_addr[3],
+		                        anycast_addr[4], anycast_addr[5]);
+	}
+
+	success = g_file_set_contents (conf_file, new_contents->str, -1, error);
+
+	g_string_free (new_contents, TRUE);
+	return success;
+}
+
+/* NM provides interface-specific options; thus the same dhclient config
+ * file cannot be used since DHCP transactions can happen in parallel.
+ * Since some distros don't have default per-interface dhclient config files,
+ * read their single config file and merge that into a custom per-interface
+ * config file along with the NM options.
+ */
+static char *
+create_dhclient_config (const char *iface,
+                        NMSettingIP4Config *s_ip4,
+                        guint8 *dhcp_anycast_addr)
+{
+	char *orig = NULL, *tmp, *conf_file = NULL;
+	GError *error = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (iface != NULL, FALSE);
+
+#if defined(TARGET_SUSE)
+	orig = g_strdup (SYSCONFDIR "/dhclient.conf");
+#elif defined(TARGET_DEBIAN) || defined(TARGET_GENTOO)
+	orig = g_strdup (SYSCONFDIR "/dhcp/dhclient.conf");
+#else
+	orig = g_strdup_printf (SYSCONFDIR "/dhclient-%s.conf", iface);
+#endif
+
+	if (!orig) {
+		nm_log_warn (LOGD_DHCP, "(%s): not enough memory for dhclient options.", iface);
+		return FALSE;
+	}
+
+#if !defined(TARGET_SUSE) && !defined(TARGET_DEBIAN) && !defined(TARGET_GENTOO)
+	/* Try /etc/dhcp/ too (rh #607759) */
+	if (!g_file_test (orig, G_FILE_TEST_EXISTS)) {
+		g_free (orig);
+		orig = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient-%s.conf", iface);
+		if (!orig) {
+			nm_log_warn (LOGD_DHCP, "(%s): not enough memory for dhclient options.", iface);
+			return FALSE;
+		}
+	}
+#endif
+
+	tmp = g_strdup_printf ("nm-dhclient-%s.conf", iface);
+	conf_file = g_build_filename ("/var", "run", tmp, NULL);
+	g_free (tmp);
+
+	error = NULL;
+	success = merge_dhclient_config (iface, conf_file, s_ip4, dhcp_anycast_addr, orig, &error);
+	if (!success) {
+		nm_log_warn (LOGD_DHCP, "(%s): error creating dhclient configuration: %s",
+		             iface, error->message);
+		g_error_free (error);
+	}
+
+	g_free (orig);
+	return conf_file;
+}
+
+
+static void
+dhclient_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static GPid
+dhclient_start (NMDHCPClient *client,
+                const char *ip_opt,
+                const char *mode_opt)
+{
+	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	GPtrArray *argv = NULL;
+	GPid pid = -1;
+	GError *error = NULL;
+	const char *iface, *uuid;
+	char *binary_name, *cmd_str;
+	gboolean ipv6;
+	guint log_domain;
+
+	g_return_val_if_fail (priv->pid_file == NULL, -1);
+	g_return_val_if_fail (ip_opt != NULL, -1);
+
+	iface = nm_dhcp_client_get_iface (client);
+	uuid = nm_dhcp_client_get_uuid (client);
+	ipv6 = nm_dhcp_client_get_ipv6 (client);
+
+	log_domain = ipv6 ? LOGD_DHCP6 : LOGD_DHCP4;
+
+	priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid",
+	                                  ipv6 ? "6" : "",
+	                                  iface);
+	if (!priv->pid_file) {
+		nm_log_warn (log_domain, "(%s): not enough memory for dhcpcd options.", iface);
+		return -1;
+	}
+
+	if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
+		nm_log_warn (log_domain, "%s does not exist.", priv->path);
+		return -1;
+	}
+
+	/* Kill any existing dhclient from the pidfile */
+	binary_name = g_path_get_basename (priv->path);
+	nm_dhcp_client_stop_existing (priv->pid_file, binary_name);
+	g_free (binary_name);
+
+	priv->lease_file = get_leasefile_for_iface (iface, uuid, ipv6);
+	if (!priv->lease_file) {
+		nm_log_warn (log_domain, "(%s): not enough memory for dhclient options.", iface);
+		return -1;
+	}
+
+	argv = g_ptr_array_new ();
+	g_ptr_array_add (argv, (gpointer) priv->path);
+
+	g_ptr_array_add (argv, (gpointer) "-d");
+
+	g_ptr_array_add (argv, (gpointer) ip_opt);
+
+	if (mode_opt)
+		g_ptr_array_add (argv, (gpointer) mode_opt);
+
+	g_ptr_array_add (argv, (gpointer) "-sf");	/* Set script file */
+	g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH );
+
+	g_ptr_array_add (argv, (gpointer) "-pf");	/* Set pid file */
+	g_ptr_array_add (argv, (gpointer) priv->pid_file);
+
+	g_ptr_array_add (argv, (gpointer) "-lf");	/* Set lease file */
+	g_ptr_array_add (argv, (gpointer) priv->lease_file);
+
+	if (priv->conf_file) {
+		g_ptr_array_add (argv, (gpointer) "-cf");	/* Set interface config file */
+		g_ptr_array_add (argv, (gpointer) priv->conf_file);
+	}
+
+	g_ptr_array_add (argv, (gpointer) iface);
+	g_ptr_array_add (argv, NULL);
+
+	cmd_str = g_strjoinv (" ", (gchar **) argv->pdata);
+	nm_log_dbg (log_domain, "running: %s", cmd_str);
+	g_free (cmd_str);
+
+	if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+	                    &dhclient_child_setup, NULL, &pid, &error)) {
+		nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message);
+		g_error_free (error);
+		pid = -1;
+	} else
+		nm_log_info (log_domain, "dhclient started with pid %d", pid);
+
+	g_ptr_array_free (argv, TRUE);
+	return pid;
+}
+
+static GPid
+real_ip4_start (NMDHCPClient *client,
+                NMSettingIP4Config *s_ip4,
+                guint8 *dhcp_anycast_addr)
+{
+	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	const char *iface;
+
+	iface = nm_dhcp_client_get_iface (client);
+
+	priv->conf_file = create_dhclient_config (iface, s_ip4, dhcp_anycast_addr);
+	if (!priv->conf_file) {
+		nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
+		return -1;
+	}
+
+	return dhclient_start (client, "-4", NULL);
+}
+
+static GPid
+real_ip6_start (NMDHCPClient *client,
+                NMSettingIP6Config *s_ip6,
+                guint8 *dhcp_anycast_addr,
+                gboolean info_only)
+{
+	return dhclient_start (client, "-6", info_only ? "-S" : "-N");
+}
+
+static void
+real_stop (NMDHCPClient *client)
+{
+	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+
+	/* Chain up to parent */
+	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client);
+
+	if (priv->conf_file)
+		remove (priv->conf_file);
+	if (priv->pid_file)
+		remove (priv->pid_file);
+}
+
+static const char **
+process_rfc3442_route (const char **octets, NMIP4Route **out_route)
+{
+	const char **o = octets;
+	int addr_len = 0, i = 0;
+	long int tmp;
+	NMIP4Route *route;
+	char *next_hop;
+	struct in_addr tmp_addr;
+
+	if (!*o)
+		return o; /* no prefix */
+
+	tmp = strtol (*o, NULL, 10);
+	if (tmp < 0 || tmp > 32)  /* 32 == max IP4 prefix length */
+		return o;
+
+	route = nm_ip4_route_new ();
+	nm_ip4_route_set_prefix (route, (guint32) tmp);
+	o++;
+
+	if (tmp > 0)
+		addr_len = ((tmp - 1) / 8) + 1;
+
+	/* ensure there's at least the address + next hop left */
+	if (g_strv_length ((char **) o) < addr_len + 4)
+		goto error;
+
+	if (tmp) {
+		const char *addr[4] = { "0", "0", "0", "0" };
+		char *str_addr;
+
+		for (i = 0; i < addr_len; i++)
+			addr[i] = *o++;
+
+		str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL);
+		if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) {
+			g_free (str_addr);
+			goto error;
+		}
+		tmp_addr.s_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp);
+		nm_ip4_route_set_dest (route, tmp_addr.s_addr);
+	}
+
+	/* Handle next hop */
+	next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL);
+	if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) {
+		g_free (next_hop);
+		goto error;
+	}
+	nm_ip4_route_set_next_hop (route, tmp_addr.s_addr);
+	g_free (next_hop);
+
+	*out_route = route;
+	return o + 4; /* advance to past the next hop */
+
+error:
+	nm_ip4_route_unref (route);
+	return o;
+}
+
+static gboolean
+real_ip4_process_classless_routes (NMDHCPClient *client,
+                                   GHashTable *options,
+                                   NMIP4Config *ip4_config,
+                                   guint32 *gwaddr)
+{
+	const char *str;
+	char **octets, **o;
+	gboolean have_routes = FALSE;
+	NMIP4Route *route = NULL;
+
+	/* dhclient doesn't have actual support for rfc3442 classless static routes
+	 * upstream.  Thus, people resort to defining the option in dhclient.conf
+	 * and using arbitrary formats like so:
+	 *
+	 * option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
+	 *
+	 * See https://lists.isc.org/pipermail/dhcp-users/2008-December/007629.html
+	 */
+
+	str = g_hash_table_lookup (options, "new_rfc3442_classless_static_routes");
+	/* Microsoft version; same as rfc3442 but with a different option # (249) */
+	if (!str)
+		str = g_hash_table_lookup (options, "new_ms_classless_static_routes");
+
+	if (!str || !strlen (str))
+		return FALSE;
+
+	o = octets = g_strsplit (str, " ", 0);
+	if (g_strv_length (octets) < 5) {
+		nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
+		goto out;
+	}
+
+	while (*o) {
+		route = NULL;
+		o = (char **) process_rfc3442_route ((const char **) o, &route);
+		if (!route) {
+			nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes");
+			break;
+		}
+
+		have_routes = TRUE;
+		if (nm_ip4_route_get_prefix (route) == 0) {
+			/* gateway passed as classless static route */
+			*gwaddr = nm_ip4_route_get_next_hop (route);
+			nm_ip4_route_unref (route);
+		} else {
+			char addr[INET_ADDRSTRLEN + 1];
+			char nh[INET_ADDRSTRLEN + 1];
+			struct in_addr tmp;
+
+			/* normal route */
+			nm_ip4_config_take_route (ip4_config, route);
+
+			tmp.s_addr = nm_ip4_route_get_dest (route);
+			inet_ntop (AF_INET, &tmp, addr, sizeof (addr));
+			tmp.s_addr = nm_ip4_route_get_next_hop (route);
+			inet_ntop (AF_INET, &tmp, nh, sizeof (nh));
+			nm_log_info (LOGD_DHCP4, "  classless static route %s/%d gw %s",
+			             addr, nm_ip4_route_get_prefix (route), nh);
+		}
+	}
+
+out:
+	g_strfreev (octets);
+	return have_routes;
+}
+
+/***************************************************/
+
+static void
+nm_dhcp_dhclient_init (NMDHCPDhclient *self)
+{
+	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
+
+	priv->path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object);
+
+	g_free (priv->pid_file);
+	g_free (priv->conf_file);
+	g_free (priv->lease_file);
+
+	G_OBJECT_CLASS (nm_dhcp_dhclient_parent_class)->dispose (object);
+}
+
+static void
+nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class)
+{
+	NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhclient_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (dhclient_class);
+
+	g_type_class_add_private (dhclient_class, sizeof (NMDHCPDhclientPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+
+	client_class->ip4_start = real_ip4_start;
+	client_class->ip6_start = real_ip6_start;
+	client_class->stop = real_stop;
+	client_class->ip4_process_classless_routes = real_ip4_process_classless_routes;
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h
new file mode 100644
index 00000000..be242de0
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-dhclient.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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) 2005 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP_DHCLIENT_H
+#define NM_DHCP_DHCLIENT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-dhcp-client.h"
+
+#define NM_TYPE_DHCP_DHCLIENT            (nm_dhcp_dhclient_get_type ())
+#define NM_DHCP_DHCLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclient))
+#define NM_DHCP_DHCLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass))
+#define NM_IS_DHCP_DHCLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCLIENT))
+#define NM_IS_DHCP_DHCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_DHCLIENT))
+#define NM_DHCP_DHCLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientClass))
+
+typedef struct {
+	NMDHCPClient parent;
+} NMDHCPDhclient;
+
+typedef struct {
+	NMDHCPClientClass parent;
+} NMDHCPDhclientClass;
+
+GType nm_dhcp_dhclient_get_type (void);
+
+GSList *nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid);
+
+const char *nm_dhcp_dhclient_get_path (const char *try_first);
+
+#endif /* NM_DHCP_DHCLIENT_H */
+
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c
new file mode 100644
index 00000000..403431fc
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c
@@ -0,0 +1,294 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-dhcp-dhcpcd.c - dhcpcd specific hooks for NetworkManager
+ *
+ * Copyright (C) 2008 Roy Marples
+ * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ */
+
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "nm-dhcp-dhcpcd.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+
+G_DEFINE_TYPE (NMDHCPDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_CLIENT)
+
+#define NM_DHCP_DHCPCD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdPrivate))
+
+#define ACTION_SCRIPT_PATH	LIBEXECDIR "/nm-dhcp-client.action"
+
+typedef struct {
+	const char *path;
+	char *pid_file;
+} NMDHCPDhcpcdPrivate;
+
+const char *
+nm_dhcp_dhcpcd_get_path (const char *try_first)
+{
+	static const char *dhcpcd_paths[] = {
+		"/sbin/dhcpcd",
+		"/usr/sbin/dhcpcd",
+		"/usr/pkg/sbin/dhcpcd",
+		"/usr/local/sbin/dhcpcd",
+		NULL
+	};
+	const char **path = dhcpcd_paths;
+
+	if (strlen (try_first) && g_file_test (try_first, G_FILE_TEST_EXISTS))
+		return try_first;
+
+	while (*path != NULL) {
+		if (g_file_test (*path, G_FILE_TEST_EXISTS))
+			break;
+		path++;
+	}
+
+	return *path;
+}
+
+GSList *
+nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid)
+{
+	return NULL;
+}
+
+static void
+dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static GPid
+real_ip4_start (NMDHCPClient *client,
+                NMSettingIP4Config *s_ip4,
+                guint8 *dhcp_anycast_addr)
+{
+	NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
+	GPtrArray *argv = NULL;
+	GPid pid = -1;
+	GError *error = NULL;
+	char *pid_contents = NULL, *binary_name, *cmd_str;
+	const char *iface, *uuid, *hostname;
+
+	g_return_val_if_fail (priv->pid_file == NULL, -1);
+
+	iface = nm_dhcp_client_get_iface (client);
+	uuid = nm_dhcp_client_get_uuid (client);
+
+	priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhcpcd-%s.pid", iface);
+	if (!priv->pid_file) {
+		nm_log_warn (LOGD_DHCP4, "(%s): not enough memory for dhcpcd options.", iface);
+		return -1;
+	}
+
+	if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
+		nm_log_warn (LOGD_DHCP4, "%s does not exist.", priv->path);
+		return -1;
+	}
+
+	/* Kill any existing dhcpcd from the pidfile */
+	binary_name = g_path_get_basename (priv->path);
+	nm_dhcp_client_stop_existing (priv->pid_file, binary_name);
+	g_free (binary_name);
+
+	argv = g_ptr_array_new ();
+	g_ptr_array_add (argv, (gpointer) priv->path);
+
+	g_ptr_array_add (argv, (gpointer) "-B");	/* Don't background on lease (disable fork()) */
+
+	g_ptr_array_add (argv, (gpointer) "-K");	/* Disable built-in carrier detection */
+
+	g_ptr_array_add (argv, (gpointer) "-L");	/* Disable built-in IPv4LL since we use avahi-autoipd */
+
+	g_ptr_array_add (argv, (gpointer) "-c");	/* Set script file */
+	g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH );
+
+	hostname = nm_setting_ip4_config_get_dhcp_hostname (s_ip4);
+	if (hostname && strlen (hostname)) {
+		g_ptr_array_add (argv, (gpointer) "-h");	/* Send hostname to DHCP server */
+		g_ptr_array_add (argv, (gpointer) hostname );
+	}
+
+	g_ptr_array_add (argv, (gpointer) iface);
+	g_ptr_array_add (argv, NULL);
+
+	cmd_str = g_strjoinv (" ", (gchar **) argv->pdata);
+	nm_log_dbg (LOGD_DHCP4, "running: %s", cmd_str);
+	g_free (cmd_str);
+
+	if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+	                    &dhcpcd_child_setup, NULL, &pid, &error)) {
+		nm_log_warn (LOGD_DHCP4, "dhcpcd failed to start.  error: '%s'", error->message);
+		g_error_free (error);
+		pid = -1;
+	} else
+		nm_log_info (LOGD_DHCP4, "dhcpcd started with pid %d", pid);
+
+	g_free (pid_contents);
+	g_ptr_array_free (argv, TRUE);
+	return pid;
+}
+
+static GPid
+real_ip6_start (NMDHCPClient *client,
+                NMSettingIP6Config *s_ip6,
+                guint8 *dhcp_anycast_addr,
+                gboolean info_only)
+{
+	nm_log_warn (LOGD_DHCP6, "the dhcpcd backend does not support IPv6.");
+	return -1;
+}
+
+static void
+real_stop (NMDHCPClient *client)
+{
+	NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
+
+	/* Chain up to parent */
+	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client);
+
+	if (priv->pid_file)
+		remove (priv->pid_file);
+}
+
+static gboolean
+real_ip4_process_classless_routes (NMDHCPClient *client,
+                                   GHashTable *options,
+                                   NMIP4Config *ip4_config,
+                                   guint32 *gwaddr)
+{
+	const char *str;
+	char **routes, **r;
+	gboolean have_routes = FALSE;
+
+	/* Classless static routes over-ride any static routes and routers
+	 * provided. We should also check for MS classless static routes as
+	 * they implemented the draft RFC using their own code.
+	 */
+	str = g_hash_table_lookup (options, "new_classless_static_routes");
+	if (!str)
+		str = g_hash_table_lookup (options, "new_ms_classless_static_routes");
+
+	if (!str || !strlen (str))
+		return FALSE;
+
+	routes = g_strsplit (str, " ", 0);
+	if (g_strv_length (routes) == 0)
+		goto out;
+
+	if ((g_strv_length (routes) % 2) != 0) {
+		nm_log_warn (LOGD_DHCP4, "  classless static routes provided, but invalid");
+		goto out;
+	}
+
+	for (r = routes; *r; r += 2) {
+		char *slash;
+		NMIP4Route *route;
+		int rt_cidr = 32;
+		struct in_addr rt_addr;
+		struct in_addr rt_route;
+
+		slash = strchr(*r, '/');
+		if (slash) {
+			*slash = '\0';
+			errno = 0;
+			rt_cidr = strtol (slash + 1, NULL, 10);
+			if ((errno == EINVAL) || (errno == ERANGE)) {
+				nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1);
+				continue;
+			}
+		}
+		if (inet_pton (AF_INET, *r, &rt_addr) <= 0) {
+			nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r);
+			continue;
+		}
+		if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) {
+			nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1));
+			continue;
+		}
+
+		have_routes = TRUE;
+		if (rt_cidr == 0 && rt_addr.s_addr == 0) {
+			/* FIXME: how to handle multiple routers? */
+			*gwaddr = rt_addr.s_addr;
+		} else {
+			route = nm_ip4_route_new ();
+			nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr);
+			nm_ip4_route_set_prefix (route, rt_cidr);
+			nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr);
+
+
+			nm_ip4_config_take_route (ip4_config, route);
+			nm_log_info (LOGD_DHCP4, "  classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1));
+		}
+	}
+
+out:
+	g_strfreev (routes);
+	return have_routes;
+}
+
+/***************************************************/
+
+static void
+nm_dhcp_dhcpcd_init (NMDHCPDhcpcd *self)
+{
+	NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self);
+
+	priv->path = nm_dhcp_dhcpcd_get_path (DHCPCD_PATH);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (object);
+
+	g_free (priv->pid_file);
+
+	G_OBJECT_CLASS (nm_dhcp_dhcpcd_parent_class)->dispose (object);
+}
+
+static void
+nm_dhcp_dhcpcd_class_init (NMDHCPDhcpcdClass *dhcpcd_class)
+{
+	NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhcpcd_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (dhcpcd_class);
+
+	g_type_class_add_private (dhcpcd_class, sizeof (NMDHCPDhcpcdPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+
+	client_class->ip4_start = real_ip4_start;
+	client_class->ip6_start = real_ip6_start;
+	client_class->stop = real_stop;
+	client_class->ip4_process_classless_routes = real_ip4_process_classless_routes;
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.h b/src/dhcp-manager/nm-dhcp-dhcpcd.h
new file mode 100644
index 00000000..586c5697
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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) 2005 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP_DHCPCD_H
+#define NM_DHCP_DHCPCD_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-dhcp-client.h"
+
+#define NM_TYPE_DHCP_DHCPCD            (nm_dhcp_dhcpcd_get_type ())
+#define NM_DHCP_DHCPCD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcd))
+#define NM_DHCP_DHCPCD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass))
+#define NM_IS_DHCP_DHCPCD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_DHCPCD))
+#define NM_IS_DHCP_DHCPCD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_DHCPCD))
+#define NM_DHCP_DHCPCD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_DHCPCD, NMDHCPDhcpcdClass))
+
+typedef struct {
+	NMDHCPClient parent;
+} NMDHCPDhcpcd;
+
+typedef struct {
+	NMDHCPClientClass parent;
+} NMDHCPDhcpcdClass;
+
+GType nm_dhcp_dhcpcd_get_type (void);
+
+GSList *nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid);
+
+const char *nm_dhcp_dhcpcd_get_path (const char *try_first);
+
+#endif /* NM_DHCP_DHCPCD_H */
+
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
new file mode 100644
index 00000000..7b110fb2
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -0,0 +1,647 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-dhcp-manager.c - Handle the DHCP daemon for 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) 2005 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ *
+ */
+
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "nm-dhcp-manager.h"
+#include "nm-dhcp-dhclient.h"
+#include "nm-dhcp-dhcpcd.h"
+#include "nm-marshal.h"
+#include "nm-logging.h"
+#include "nm-dbus-manager.h"
+#include "nm-hostname-provider.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GQuark
+nm_dhcp_manager_error_quark (void)
+{
+    static GQuark ret = 0;
+
+    if (ret == 0)
+        ret = g_quark_from_static_string ("nm_dhcp_manager_error");
+
+    return ret;
+}
+
+GType
+nm_dhcp_manager_error_get_type (void)
+{
+    static GType etype = 0;
+
+    if (etype == 0) {
+        static const GEnumValue values[] = {
+            ENUM_ENTRY (NM_DHCP_MANAGER_ERROR_BAD_CLIENT, "BadClient"),
+            ENUM_ENTRY (NM_DHCP_MANAGER_ERROR_INTERNAL,   "InternalError"),
+            { 0, 0, 0 }
+        };
+        etype = g_enum_register_static ("NMDhcpManagerError", values);
+    }
+    return etype;
+}
+
+#define NM_DHCP_CLIENT_DBUS_SERVICE "org.freedesktop.nm_dhcp_client"
+#define NM_DHCP_CLIENT_DBUS_IFACE   "org.freedesktop.nm_dhcp_client"
+
+#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */
+
+static NMDHCPManager *singleton = NULL;
+
+typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid);
+
+typedef struct {
+	GType               client_type;
+	GetLeaseConfigFunc  get_lease_config_func;
+
+	NMDBusManager *     dbus_mgr;
+	GHashTable *        clients;
+	DBusGProxy *        proxy;
+	NMHostnameProvider *hostname_provider;
+} NMDHCPManagerPrivate;
+
+
+#define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDHCPManagerPrivate))
+
+G_DEFINE_TYPE (NMDHCPManager, nm_dhcp_manager, G_TYPE_OBJECT)
+
+static char *
+garray_to_string (GArray *array, const char *key)
+{
+	GString *str;
+	int i;
+	unsigned char c;
+	char *converted = NULL;
+
+	g_return_val_if_fail (array != NULL, NULL);
+
+	/* Since the DHCP options come through environment variables, they should
+	 * already be UTF-8 safe, but just make sure.
+	 */
+	str = g_string_sized_new (array->len);
+	for (i = 0; i < array->len; i++) {
+		c = array->data[i];
+
+		/* Convert NULLs to spaces and non-ASCII characters to ? */
+		if (c == '\0')
+			c = ' ';
+		else if (c > 127)
+			c = '?';
+		str = g_string_append_c (str, c);
+	}
+	str = g_string_append_c (str, '\0');
+
+	converted = str->str;
+	if (!g_utf8_validate (converted, -1, NULL))
+		nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
+	g_string_free (str, FALSE);
+	return converted;
+}
+
+static NMDHCPClient *
+get_client_for_pid (NMDHCPManager *manager, GPid pid)
+{
+	NMDHCPManagerPrivate *priv;
+	GHashTableIter iter;
+	gpointer value;
+
+	g_return_val_if_fail (manager != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
+
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+
+	g_hash_table_iter_init (&iter, priv->clients);
+	while (g_hash_table_iter_next (&iter, NULL, &value)) {
+		NMDHCPClient *candidate = NM_DHCP_CLIENT (value);
+
+		if (nm_dhcp_client_get_pid (candidate) == pid)
+			return candidate;
+	}
+
+	return NULL;
+}
+
+static NMDHCPClient *
+get_client_for_iface (NMDHCPManager *manager,
+                      const char *iface,
+                      gboolean ip6)
+{
+	NMDHCPManagerPrivate *priv;
+	GHashTableIter iter;
+	gpointer value;
+
+	g_return_val_if_fail (manager != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
+	g_return_val_if_fail (iface, NULL);
+
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+
+	g_hash_table_iter_init (&iter, priv->clients);
+	while (g_hash_table_iter_next (&iter, NULL, &value)) {
+		NMDHCPClient *candidate = NM_DHCP_CLIENT (value);
+
+		if (   !strcmp (iface, nm_dhcp_client_get_iface (candidate))
+		    && (nm_dhcp_client_get_ipv6 (candidate) == ip6))
+			return candidate;
+	}
+
+	return NULL;
+}
+
+static char *
+get_option (GHashTable *hash, const char *key)
+{
+	GValue *value;
+
+	value = g_hash_table_lookup (hash, key);
+	if (value == NULL)
+		return NULL;
+
+	if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
+		nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
+		             "DBUS_TYPE_G_UCHAR_ARRAY",
+		             (char *) key);
+		return NULL;
+	}
+
+	return garray_to_string ((GArray *) g_value_get_boxed (value), key);
+}
+
+static void
+nm_dhcp_manager_handle_event (DBusGProxy *proxy,
+                              GHashTable *options,
+                              gpointer user_data)
+{
+	NMDHCPManager *manager;
+	NMDHCPManagerPrivate *priv;
+	NMDHCPClient *client;
+	char *iface = NULL;
+	char *pid_str = NULL;
+	char *reason = NULL;
+	unsigned long temp;
+
+	manager = NM_DHCP_MANAGER (user_data);
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+
+	iface = get_option (options, "interface");
+	if (iface == NULL) {
+		nm_log_warn (LOGD_DHCP, "DHCP event didn't have associated interface.");
+		goto out;
+	}
+
+	pid_str = get_option (options, "pid");
+	if (pid_str == NULL) {
+		nm_log_warn (LOGD_DHCP, "DHCP event didn't have associated PID.");
+		goto out;
+	}
+
+	temp = strtoul (pid_str, NULL, 10);
+	if ((temp == ULONG_MAX) && (errno == ERANGE)) {
+		nm_log_warn (LOGD_DHCP, "couldn't convert PID");
+		goto out;
+	}
+
+	client = get_client_for_pid (manager, (GPid) temp);
+	if (client == NULL) {
+		nm_log_warn (LOGD_DHCP, "(pid %d) unhandled DHCP event for interface %s", temp, iface);
+		goto out;
+	}
+
+	if (strcmp (iface, nm_dhcp_client_get_iface (client))) {
+		nm_log_warn (LOGD_DHCP, "(pid %d) received DHCP event from unexpected interface '%s' (expected '%s')",
+		             temp, iface, nm_dhcp_client_get_iface (client));
+		goto out;
+	}
+
+	reason = get_option (options, "reason");
+	if (reason == NULL) {
+		nm_log_warn (LOGD_DHCP, "(pid %d) DHCP event didn't have a reason", temp);
+		goto out;
+	}
+
+	nm_dhcp_client_new_options (client, options, reason);
+
+out:
+	g_free (iface);
+	g_free (pid_str);
+	g_free (reason);
+}
+
+static GType
+get_client_type (const char *client, GError **error)
+{
+	const char *dhclient_path = NULL;
+	const char *dhcpcd_path = NULL;
+
+	dhclient_path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH);
+	dhcpcd_path = nm_dhcp_dhcpcd_get_path (DHCPCD_PATH);
+
+	if (!client) {
+		if (dhclient_path)
+			return NM_TYPE_DHCP_DHCLIENT;
+		else if (dhcpcd_path)
+			return NM_TYPE_DHCP_DHCPCD;
+		else {
+			g_set_error_literal (error,
+			                     NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
+			                     _("no usable DHCP client could be found."));
+			return 0;
+		}
+	}
+
+	if (!strcmp (client, "dhclient")) {
+		if (!dhclient_path) {
+			g_set_error_literal (error,
+			                     NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
+			                     _("'dhclient' could be found."));
+			return 0;
+		}
+		return NM_TYPE_DHCP_DHCLIENT;
+	}
+
+	if (!strcmp (client, "dhcpcd")) {
+		if (!dhcpcd_path) {
+			g_set_error_literal (error,
+			                     NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
+			                     _("'dhcpcd' could be found."));
+			return 0;
+		}
+		return NM_TYPE_DHCP_DHCPCD;
+	}
+
+	g_set_error (error,
+	             NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
+	             _("unsupported DHCP client '%s'"), client);
+	return 0;
+}
+
+NMDHCPManager *
+nm_dhcp_manager_new (const char *client, GError **error)
+{
+	NMDHCPManagerPrivate *priv;
+	DBusGConnection *g_connection;
+	GType client_type;
+
+	client_type = get_client_type (client, error);
+	if (!client_type)
+		return NULL;
+
+	g_warn_if_fail (singleton == NULL);
+
+	singleton = g_object_new (NM_TYPE_DHCP_MANAGER, NULL);
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (singleton);
+
+	/* Client-specific setup */
+	priv->client_type = client_type;
+	if (priv->client_type == NM_TYPE_DHCP_DHCLIENT)
+		priv->get_lease_config_func = nm_dhcp_dhclient_get_lease_config;
+	else if (priv->client_type == NM_TYPE_DHCP_DHCPCD)
+		priv->get_lease_config_func = nm_dhcp_dhcpcd_get_lease_config;
+	else
+		g_assert_not_reached ();
+
+	priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+	                                       NULL,
+	                                       (GDestroyNotify) g_object_unref);
+	g_assert (priv->clients);
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	priv->proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                         NM_DHCP_CLIENT_DBUS_SERVICE,
+	                                         "/",
+	                                         NM_DHCP_CLIENT_DBUS_IFACE);
+	g_assert (priv->proxy);
+
+	dbus_g_proxy_add_signal (priv->proxy,
+	                         "Event",
+	                         DBUS_TYPE_G_MAP_OF_VARIANT,
+	                         G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal (priv->proxy, "Event",
+	                             G_CALLBACK (nm_dhcp_manager_handle_event),
+	                             singleton,
+	                             NULL);
+
+	return singleton;
+}
+
+#define REMOVE_ID_TAG "remove-id"
+#define TIMEOUT_ID_TAG "timeout-id"
+
+static void
+remove_client (NMDHCPManager *self, NMDHCPClient *client)
+{
+	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+	guint id;
+
+	id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), REMOVE_ID_TAG));
+	if (id)
+		g_signal_handler_disconnect (client, id);
+
+	id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), TIMEOUT_ID_TAG));
+	if (id)
+		g_signal_handler_disconnect (client, id);
+
+	/* Stopping the client is left up to the controlling device
+	 * explicitly since we may want to quit NetworkManager but not terminate
+	 * the DHCP client.
+	 */
+
+	g_hash_table_remove (priv->clients, client);
+}
+
+static void
+add_client (NMDHCPManager *self, NMDHCPClient *client)
+{
+	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+	guint id;
+
+	id = g_signal_connect_swapped (client, "remove", G_CALLBACK (remove_client), self);
+	g_object_set_data (G_OBJECT (client), REMOVE_ID_TAG, GUINT_TO_POINTER (id));
+
+	id = g_signal_connect_swapped (client, "timeout", G_CALLBACK (remove_client), self);
+	g_object_set_data (G_OBJECT (client), TIMEOUT_ID_TAG, GUINT_TO_POINTER (id));
+
+	g_hash_table_insert (priv->clients, client, g_object_ref (client));
+}
+
+static NMDHCPClient *
+client_start (NMDHCPManager *self,
+              const char *iface,
+              const char *uuid,
+              gboolean ipv6,
+              NMSettingIP4Config *s_ip4,
+              NMSettingIP6Config *s_ip6,
+              guint32 timeout,
+              guint8 *dhcp_anycast_addr,
+              gboolean info_only)
+{
+	NMDHCPManagerPrivate *priv;
+	NMDHCPClient *client;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (self, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+	g_return_val_if_fail (iface != NULL, NULL);
+	g_return_val_if_fail (uuid != NULL, NULL);
+
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+
+	/* Kill any old client instance */
+	client = get_client_for_iface (self, iface, ipv6);
+	if (client) {
+		nm_dhcp_client_stop (client);
+		remove_client (self, client);
+	}
+
+	/* And make a new one */
+	client = g_object_new (priv->client_type,
+	                       NM_DHCP_CLIENT_INTERFACE, iface,
+	                       NM_DHCP_CLIENT_IPV6, ipv6,
+	                       NM_DHCP_CLIENT_UUID, uuid,
+	                       NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT,
+	                       NULL);
+	g_return_val_if_fail (client != NULL, NULL);
+	add_client (self, client);
+
+	if (ipv6)
+		success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr, info_only);
+	else
+		success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr);
+
+	if (!success) {
+		remove_client (self, client);
+		g_object_unref (client);
+		client = NULL;
+	}
+
+	return client;
+}
+
+/* Caller owns a reference to the NMDHCPClient on return */
+NMDHCPClient *
+nm_dhcp_manager_start_ip4 (NMDHCPManager *self,
+                           const char *iface,
+                           const char *uuid,
+                           NMSettingIP4Config *s_ip4,
+                           guint32 timeout,
+                           guint8 *dhcp_anycast_addr)
+{
+	NMDHCPManagerPrivate *priv;
+	NMDHCPClient *client = NULL;
+
+	g_return_val_if_fail (self, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+
+	if (s_ip4) {
+		const char *method = nm_setting_ip4_config_get_method (s_ip4);
+
+		if (method) {
+			/* Method must be 'auto' */
+			g_return_val_if_fail (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0, NULL);
+		}
+
+		if (   nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4)
+		    && (nm_setting_ip4_config_get_dhcp_hostname (s_ip4) == NULL)
+		    && priv->hostname_provider != NULL) {
+
+			s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_duplicate (NM_SETTING (s_ip4)));
+
+			/* We're asked to send the hostname to DHCP server, the hostname
+			 * isn't specified, and a hostname provider is registered: use that
+			 */
+			g_object_set (G_OBJECT (s_ip4),
+						  NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME,
+						  nm_hostname_provider_get_hostname (priv->hostname_provider),
+						  NULL);
+		} else
+			g_object_ref (s_ip4);
+	}
+
+	client = client_start (self, iface, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr, FALSE);
+
+	if (s_ip4)
+		g_object_unref (s_ip4);
+
+	return client;
+}
+
+/* Caller owns a reference to the NMDHCPClient on return */
+NMDHCPClient *
+nm_dhcp_manager_start_ip6 (NMDHCPManager *self,
+                           const char *iface,
+                           const char *uuid,
+                           NMSettingIP6Config *s_ip6,
+                           guint32 timeout,
+                           guint8 *dhcp_anycast_addr,
+                           gboolean info_only)
+{
+	return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, info_only);
+}
+
+static void
+hostname_provider_destroyed (gpointer data, GObject *destroyed_object)
+{
+	NM_DHCP_MANAGER_GET_PRIVATE (data)->hostname_provider = NULL;
+}
+
+void
+nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager,
+									   NMHostnameProvider *provider)
+{
+	NMDHCPManagerPrivate *priv;
+
+	g_return_if_fail (NM_IS_DHCP_MANAGER (manager));
+
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
+
+	if (priv->hostname_provider) {
+		g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, manager);
+		priv->hostname_provider = NULL;
+	}
+
+	if (provider) {
+		priv->hostname_provider = provider;
+		g_object_weak_ref (G_OBJECT (provider), hostname_provider_destroyed, manager);
+	}
+}
+
+GSList *
+nm_dhcp_manager_get_lease_config (NMDHCPManager *self,
+                                  const char *iface,
+                                  const char *uuid)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+	g_return_val_if_fail (iface != NULL, NULL);
+	g_return_val_if_fail (uuid != NULL, NULL);
+
+	return NM_DHCP_MANAGER_GET_PRIVATE (self)->get_lease_config_func (iface, uuid);
+}
+
+NMIP4Config *
+nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,
+                                            const char *iface,
+                                            GHashTable *options,
+                                            const char *reason)
+{
+	NMDHCPClient *client;
+	NMIP4Config *config;
+	GType client_type;
+	GError *error = NULL;
+
+	client_type = get_client_type (dhcp_client, &error);
+	if (!client_type) {
+		nm_log_err (LOGD_DHCP4, "error: %s", error ? error->message : "(unknown)");
+		g_clear_error (&error);
+		return NULL;
+	}
+
+	client = (NMDHCPClient *) g_object_new (client_type,
+	                                        NM_DHCP_CLIENT_INTERFACE, iface,
+	                                        NULL);
+	g_return_val_if_fail (client != NULL, NULL);
+	nm_dhcp_client_new_options (client, options, reason);
+	config = nm_dhcp_client_get_ip4_config (client, TRUE);
+	g_object_unref (client);
+
+	return config;
+}
+
+/***************************************************/
+
+NMDHCPManager *
+nm_dhcp_manager_get (void)
+{
+	g_warn_if_fail (singleton != NULL);
+	return g_object_ref (singleton);
+}
+
+static void
+nm_dhcp_manager_init (NMDHCPManager *manager)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
+	GList *values, *iter;
+
+	if (priv->clients) {
+		values = g_hash_table_get_values (priv->clients);
+		for (iter = values; iter; iter = g_list_next (iter))
+			remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data));
+		g_list_free (values);
+	}
+
+	G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
+
+	if (priv->hostname_provider) {
+		g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, object);
+		priv->hostname_provider = NULL;
+	}
+
+	if (priv->clients)
+		g_hash_table_destroy (priv->clients);
+	if (priv->proxy)
+		g_object_unref (priv->proxy);
+	if (priv->dbus_mgr)
+		g_object_unref (priv->dbus_mgr);
+
+	G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object);
+}
+
+static void
+nm_dhcp_manager_class_init (NMDHCPManagerClass *manager_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+	g_type_class_add_private (manager_class, sizeof (NMDHCPManagerPrivate));
+
+	/* virtual methods */
+	object_class->finalize = finalize;
+	object_class->dispose = dispose;
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h
new file mode 100644
index 00000000..9d83d096
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-manager.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-dhcp-manager.c - Handle the DHCP daemon for 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) 2005 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_DHCP_MANAGER_H
+#define NM_DHCP_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
+
+#include "nm-dhcp-client.h"
+#include "nm-ip4-config.h"
+#include "nm-dhcp4-config.h"
+#include "nm-hostname-provider.h"
+
+enum {
+    NM_DHCP_MANAGER_ERROR_BAD_CLIENT = 0,
+    NM_DHCP_MANAGER_ERROR_INTERNAL = 1,
+};
+
+#define NM_DHCP_MANAGER_ERROR (nm_dhcp_manager_error_quark ())
+#define NN_TYPE_DHCP_MANAGER_ERROR (nm_dhcp_manager_error_get_type ())
+
+GQuark nm_dhcp_manager_error_quark    (void);
+GType  nm_dhcp_manager_error_get_type (void);
+
+
+#define NM_TYPE_DHCP_MANAGER            (nm_dhcp_manager_get_type ())
+#define NM_DHCP_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManager))
+#define NM_DHCP_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass))
+#define NM_IS_DHCP_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_MANAGER))
+#define NM_IS_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_MANAGER))
+#define NM_DHCP_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass))
+
+typedef struct {
+	GObject parent;
+} NMDHCPManager;
+
+typedef struct {
+	GObjectClass parent;
+} NMDHCPManagerClass;
+
+GType nm_dhcp_manager_get_type (void);
+
+NMDHCPManager *nm_dhcp_manager_get                  (void);
+
+void           nm_dhcp_manager_set_hostname_provider(NMDHCPManager *manager,
+													 NMHostnameProvider *provider);
+
+NMDHCPClient * nm_dhcp_manager_start_ip4     (NMDHCPManager *manager,
+                                              const char *iface,
+                                              const char *uuid,
+                                              NMSettingIP4Config *s_ip4,
+                                              guint32 timeout,
+                                              guint8 *dhcp_anycast_addr);
+
+NMDHCPClient * nm_dhcp_manager_start_ip6     (NMDHCPManager *manager,
+                                              const char *iface,
+                                              const char *uuid,
+                                              NMSettingIP6Config *s_ip6,
+                                              guint32 timeout,
+                                              guint8 *dhcp_anycast_addr,
+                                              gboolean info_only);
+
+GSList *       nm_dhcp_manager_get_lease_config (NMDHCPManager *self,
+                                                 const char *iface,
+                                                 const char *uuid);
+
+/* For testing only */
+NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,
+                                                         const char *iface,
+                                                         GHashTable *options,
+                                                         const char *reason);
+
+/* Only for main.c */
+NMDHCPManager *nm_dhcp_manager_new (const char *client, GError **error);
+
+#endif /* NM_DHCP_MANAGER_H */
diff --git a/src/dnsmasq-manager/Makefile.am b/src/dnsmasq-manager/Makefile.am
new file mode 100644
index 00000000..66bbdd83
--- /dev/null
+++ b/src/dnsmasq-manager/Makefile.am
@@ -0,0 +1,19 @@
+INCLUDES = \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/include
+
+noinst_LTLIBRARIES = libdnsmasq-manager.la
+
+libdnsmasq_manager_la_SOURCES = \
+	nm-dnsmasq-manager.h \
+	nm-dnsmasq-manager.c
+
+libdnsmasq_manager_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	-DLOCALSTATEDIR=\"$(localstatedir)\"
+
+libdnsmasq_manager_la_LIBADD = \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(GLIB_LIBS)
diff --git a/src/dnsmasq-manager/Makefile.in b/src/dnsmasq-manager/Makefile.in
new file mode 100644
index 00000000..c71621a8
--- /dev/null
+++ b/src/dnsmasq-manager/Makefile.in
@@ -0,0 +1,564 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/dnsmasq-manager
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libdnsmasq_manager_la_DEPENDENCIES =  \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1)
+am_libdnsmasq_manager_la_OBJECTS =  \
+	libdnsmasq_manager_la-nm-dnsmasq-manager.lo
+libdnsmasq_manager_la_OBJECTS = $(am_libdnsmasq_manager_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libdnsmasq_manager_la_SOURCES)
+DIST_SOURCES = $(libdnsmasq_manager_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/include
+
+noinst_LTLIBRARIES = libdnsmasq-manager.la
+libdnsmasq_manager_la_SOURCES = \
+	nm-dnsmasq-manager.h \
+	nm-dnsmasq-manager.c
+
+libdnsmasq_manager_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	-DLOCALSTATEDIR=\"$(localstatedir)\"
+
+libdnsmasq_manager_la_LIBADD = \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(GLIB_LIBS)
+
+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/dnsmasq-manager/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/dnsmasq-manager/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libdnsmasq-manager.la: $(libdnsmasq_manager_la_OBJECTS) $(libdnsmasq_manager_la_DEPENDENCIES) 
+	$(LINK)  $(libdnsmasq_manager_la_OBJECTS) $(libdnsmasq_manager_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdnsmasq_manager_la-nm-dnsmasq-manager.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libdnsmasq_manager_la-nm-dnsmasq-manager.lo: nm-dnsmasq-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnsmasq_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnsmasq_manager_la-nm-dnsmasq-manager.lo -MD -MP -MF $(DEPDIR)/libdnsmasq_manager_la-nm-dnsmasq-manager.Tpo -c -o libdnsmasq_manager_la-nm-dnsmasq-manager.lo `test -f 'nm-dnsmasq-manager.c' || echo '$(srcdir)/'`nm-dnsmasq-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libdnsmasq_manager_la-nm-dnsmasq-manager.Tpo $(DEPDIR)/libdnsmasq_manager_la-nm-dnsmasq-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-dnsmasq-manager.c' object='libdnsmasq_manager_la-nm-dnsmasq-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnsmasq_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnsmasq_manager_la-nm-dnsmasq-manager.lo `test -f 'nm-dnsmasq-manager.c' || echo '$(srcdir)/'`nm-dnsmasq-manager.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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-noinstLTLIBRARIES \
+	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: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 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/dnsmasq-manager/nm-dnsmasq-manager.c b/src/dnsmasq-manager/nm-dnsmasq-manager.c
new file mode 100644
index 00000000..e6c67a1e
--- /dev/null
+++ b/src/dnsmasq-manager/nm-dnsmasq-manager.c
@@ -0,0 +1,495 @@
+/* -*- 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) 2008 - 2010 Red Hat, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include "nm-dnsmasq-manager.h"
+#include "nm-logging.h"
+#include "nm-glib-compat.h"
+
+typedef struct {
+	char *iface;
+	char *pidfile;
+	GPid pid;
+	guint32 dm_watch_id;
+} NMDnsMasqManagerPrivate;
+
+#define NM_DNSMASQ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManagerPrivate))
+
+G_DEFINE_TYPE (NMDnsMasqManager, nm_dnsmasq_manager, G_TYPE_OBJECT)
+
+enum {
+	STATE_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef enum {
+	NM_DNSMASQ_MANAGER_ERROR_NOT_FOUND,
+} NMDnsMasqManagerError;
+
+GQuark
+nm_dnsmasq_manager_error_quark (void)
+{
+	static GQuark quark;
+
+	if (!quark)
+		quark = g_quark_from_static_string ("nm_dnsmasq_manager_error");
+
+	return quark;
+}
+
+static void
+nm_dnsmasq_manager_init (NMDnsMasqManager *manager)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (object);
+
+	nm_dnsmasq_manager_stop (NM_DNSMASQ_MANAGER (object));
+
+	g_free (priv->iface);
+	g_free (priv->pidfile);
+
+	G_OBJECT_CLASS (nm_dnsmasq_manager_parent_class)->finalize (object);
+}
+
+static void
+nm_dnsmasq_manager_class_init (NMDnsMasqManagerClass *manager_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+	g_type_class_add_private (manager_class, sizeof (NMDnsMasqManagerPrivate));
+
+	object_class->finalize = finalize;
+
+	/* signals */
+	signals[STATE_CHANGED] =
+		g_signal_new ("state-changed",
+				    G_OBJECT_CLASS_TYPE (object_class),
+				    G_SIGNAL_RUN_FIRST,
+				    G_STRUCT_OFFSET (NMDnsMasqManagerClass, state_changed),
+				    NULL, NULL,
+				    g_cclosure_marshal_VOID__UINT,
+				    G_TYPE_NONE, 1,
+				    G_TYPE_UINT);
+}
+
+NMDnsMasqManager *
+nm_dnsmasq_manager_new (const char *iface)
+{
+	NMDnsMasqManager *manager;
+	NMDnsMasqManagerPrivate *priv;
+
+	manager = (NMDnsMasqManager *) g_object_new (NM_TYPE_DNSMASQ_MANAGER, NULL);
+	if (!manager)
+		return NULL;
+
+	priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
+	priv->iface = g_strdup (iface);
+	priv->pidfile = g_strdup_printf (LOCALSTATEDIR "/run/nm-dnsmasq-%s.pid", iface);
+
+	return manager;
+}
+
+typedef struct {
+	GPtrArray *array;
+	GStringChunk *chunk;
+} NMCmdLine;
+
+static NMCmdLine *
+nm_cmd_line_new (void)
+{
+	NMCmdLine *cmd;
+
+	cmd = g_slice_new (NMCmdLine);
+	cmd->array = g_ptr_array_new ();
+	cmd->chunk = g_string_chunk_new (1024);
+
+	return cmd;
+}
+
+static void
+nm_cmd_line_destroy (NMCmdLine *cmd)
+{
+	g_ptr_array_free (cmd->array, TRUE);
+	g_string_chunk_free (cmd->chunk);
+	g_slice_free (NMCmdLine, cmd);
+}
+
+static char *
+nm_cmd_line_to_str (NMCmdLine *cmd)
+{
+	char *str;
+
+	g_ptr_array_add (cmd->array, NULL);
+	str = g_strjoinv (" ", (gchar **) cmd->array->pdata);
+	g_ptr_array_remove_index (cmd->array, cmd->array->len - 1);
+
+	return str;
+}
+
+static void
+nm_cmd_line_add_string (NMCmdLine *cmd, const char *str)
+{
+	g_ptr_array_add (cmd->array, g_string_chunk_insert (cmd->chunk, str));
+}
+
+/*******************************************/
+
+static inline const char *
+nm_find_dnsmasq (void)
+{
+	static const char *dnsmasq_binary_paths[] = {
+		"/usr/local/sbin/dnsmasq",
+		"/usr/sbin/dnsmasq",
+		"/sbin/dnsmasq",
+		NULL
+	};
+
+	const char **dnsmasq_binary = dnsmasq_binary_paths;
+
+	while (*dnsmasq_binary != NULL) {
+		if (g_file_test (*dnsmasq_binary, G_FILE_TEST_EXISTS))
+			break;
+		dnsmasq_binary++;
+	}
+
+	return *dnsmasq_binary;
+}
+
+static void
+dm_exit_code (guint dm_exit_status)
+{
+	char *msg = "Unknown error";
+
+	switch (dm_exit_status) {
+	case 1:
+		msg = "Configuration problem";
+		break;
+	case 2:
+		msg = "Network access problem (address in use; permissions; etc)";
+		break;
+	case 3:
+		msg = "Filesystem problem (missing file/directory; permissions; etc)";
+		break;
+	case 4:
+		msg = "Memory allocation failure";
+		break;
+	case 5: 
+		msg = "Other problem";
+		break;
+	default:
+		if (dm_exit_status >= 11)
+			msg = "Lease-script 'init' process failure";
+		break;
+	}
+
+	nm_log_warn (LOGD_SHARING, "dnsmasq exited with error: %s (%d)", msg, dm_exit_status);
+}
+
+static void
+dm_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+	NMDnsMasqManager *manager = NM_DNSMASQ_MANAGER (user_data);
+	NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
+	guint err;
+
+	if (WIFEXITED (status)) {
+		err = WEXITSTATUS (status);
+		if (err != 0)
+			dm_exit_code (err);
+	} else if (WIFSTOPPED (status)) {
+		nm_log_warn (LOGD_SHARING, "dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
+	} else if (WIFSIGNALED (status)) {
+		nm_log_warn (LOGD_SHARING, "dnsmasq died with signal %d", WTERMSIG (status));
+	} else {
+		nm_log_warn (LOGD_SHARING, "dnsmasq died from an unknown cause");
+	}
+  
+	priv->pid = 0;
+
+	g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_DNSMASQ_STATUS_DEAD);
+}
+
+static NMCmdLine *
+create_dm_cmd_line (const char *iface,
+                    NMIP4Config *ip4_config,
+                    const char *pidfile,
+                    GError **error)
+{
+	const char *dm_binary;
+	NMCmdLine *cmd;
+	GString *s;
+	NMIP4Address *tmp;
+	struct in_addr addr;
+	char buf[INET_ADDRSTRLEN + 15];
+	char localaddr[INET_ADDRSTRLEN + 1];
+	int i;
+
+	dm_binary = nm_find_dnsmasq ();
+	if (!dm_binary) {
+		g_set_error (error, NM_DNSMASQ_MANAGER_ERROR, NM_DNSMASQ_MANAGER_ERROR_NOT_FOUND,
+		             "Could not find dnsmasq binary.");
+		return NULL;
+	}
+
+	/* Find the IP4 address to use */
+	tmp = nm_ip4_config_get_address (ip4_config, 0);
+
+	/* Create dnsmasq command line */
+	cmd = nm_cmd_line_new ();
+	nm_cmd_line_add_string (cmd, dm_binary);
+
+	if (getenv ("NM_DNSMASQ_DEBUG")) {
+		nm_cmd_line_add_string (cmd, "--log-dhcp");
+		nm_cmd_line_add_string (cmd, "--log-queries");
+	}
+
+	/* dnsmasq may read from it's default config file location, which if that
+	 * location is a valid config file, it will combine with the options here
+	 * and cause undesirable side-effects.  Like sending bogus IP addresses
+	 * as the gateway or whatever.  So give dnsmasq a bogus config file
+	 * location to avoid screwing up the configuration we're passing to it.
+	 */
+	memset (buf, 0, sizeof (buf));
+	strcpy (buf, "/tmp/");
+	for (i = 5; i < 15; i++)
+		buf[i] = (char) (g_random_int_range ((guint32) 'a', (guint32) 'z') & 0xFF);
+	strcat (buf, ".conf");
+
+	nm_cmd_line_add_string (cmd, "--conf-file");
+	nm_cmd_line_add_string (cmd, buf);
+
+	nm_cmd_line_add_string (cmd, "--no-hosts");
+	nm_cmd_line_add_string (cmd, "--keep-in-foreground");
+	nm_cmd_line_add_string (cmd, "--bind-interfaces");
+	nm_cmd_line_add_string (cmd, "--except-interface=lo");
+	nm_cmd_line_add_string (cmd, "--clear-on-reload");
+
+	/* Use strict order since in the case of VPN connections, the VPN's
+	 * nameservers will be first in resolv.conf, and those need to be tried
+	 * first by dnsmasq to successfully resolve names from the VPN.
+	 */
+	nm_cmd_line_add_string (cmd, "--strict-order");
+
+	s = g_string_new ("--listen-address=");
+	addr.s_addr = nm_ip4_address_get_address (tmp);
+	if (!inet_ntop (AF_INET, &addr, &localaddr[0], INET_ADDRSTRLEN)) {
+		nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X",
+		             ntohl (addr.s_addr));
+		goto error;
+	}
+	g_string_append (s, localaddr);
+	nm_cmd_line_add_string (cmd, s->str);
+	g_string_free (s, TRUE);
+
+	s = g_string_new ("--dhcp-range=");
+
+	/* Add start of address range */
+	addr.s_addr = nm_ip4_address_get_address (tmp) + htonl (9);
+	if (!inet_ntop (AF_INET, &addr, &buf[0], INET_ADDRSTRLEN)) {
+		nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X",
+		             ntohl (addr.s_addr));
+		goto error;
+	}
+	g_string_append (s, buf);
+
+	g_string_append_c (s, ',');
+
+	/* Add end of address range */
+	addr.s_addr = nm_ip4_address_get_address (tmp) + htonl (99);
+	if (!inet_ntop (AF_INET, &addr, &buf[0], INET_ADDRSTRLEN)) {
+		nm_log_warn (LOGD_SHARING, "error converting IP4 address 0x%X",
+		             ntohl (addr.s_addr));
+		goto error;
+	}
+	g_string_append (s, buf);
+
+	g_string_append (s, ",60m");
+	nm_cmd_line_add_string (cmd, s->str);
+	g_string_free (s, TRUE);
+
+	s = g_string_new ("--dhcp-option=option:router,");
+	g_string_append (s, localaddr);
+	nm_cmd_line_add_string (cmd, s->str);
+	g_string_free (s, TRUE);
+
+	nm_cmd_line_add_string (cmd, "--dhcp-lease-max=50");
+
+	s = g_string_new ("--pid-file=");
+	g_string_append (s, pidfile);
+	nm_cmd_line_add_string (cmd, s->str);
+	g_string_free (s, TRUE);
+
+	return cmd;
+
+error:
+	nm_cmd_line_destroy (cmd);
+	return NULL;
+}
+
+static void
+dm_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static void
+kill_existing_for_iface (const char *iface, const char *pidfile)
+{
+	char *contents = NULL;
+	glong pid;
+	char *proc_path = NULL;
+	char *cmdline_contents = NULL;
+
+	if (!g_file_get_contents (pidfile, &contents, NULL, NULL))
+		goto out;
+
+	pid = strtol (contents, NULL, 10);
+	if (pid < 1 || pid > INT_MAX)
+		goto out;
+
+	proc_path = g_strdup_printf ("/proc/%ld/cmdline", pid);
+	if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL))
+		goto out;
+
+	if (strstr (cmdline_contents, "bin/dnsmasq")) {
+		if (kill (pid, 0)) {
+			nm_log_dbg (LOGD_SHARING, "Killing stale dnsmasq process %ld", pid);
+			kill (pid, SIGKILL);
+		}
+		unlink (pidfile);
+	}
+
+out:
+	g_free (cmdline_contents);
+	g_free (proc_path);
+	g_free (contents);
+}
+
+gboolean
+nm_dnsmasq_manager_start (NMDnsMasqManager *manager,
+                          NMIP4Config *ip4_config,
+                          GError **error)
+{
+	NMDnsMasqManagerPrivate *priv;
+	NMCmdLine *dm_cmd;
+	char *cmd_str;
+
+	g_return_val_if_fail (NM_IS_DNSMASQ_MANAGER (manager), FALSE);
+	if (error)
+		g_return_val_if_fail (*error == NULL, FALSE);
+
+	priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
+
+	kill_existing_for_iface (priv->iface, priv->pidfile);
+
+	dm_cmd = create_dm_cmd_line (priv->iface, ip4_config, priv->pidfile, error);
+	if (!dm_cmd)
+		return FALSE;
+
+	g_ptr_array_add (dm_cmd->array, NULL);
+
+	nm_log_info (LOGD_SHARING, "Starting dnsmasq...");
+
+	cmd_str = nm_cmd_line_to_str (dm_cmd);
+	nm_log_dbg (LOGD_SHARING, "Command line: %s", cmd_str);
+	g_free (cmd_str);
+
+	priv->pid = 0;
+	if (!g_spawn_async (NULL, (char **) dm_cmd->array->pdata, NULL,
+					G_SPAWN_DO_NOT_REAP_CHILD,
+					dm_child_setup,
+					NULL, &priv->pid, error)) {
+		goto out;
+	}
+
+	nm_log_dbg (LOGD_SHARING, "dnsmasq started with pid %d", priv->pid);
+
+	priv->dm_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) dm_watch_cb, manager);
+
+ out:
+	if (dm_cmd)
+		nm_cmd_line_destroy (dm_cmd);
+
+	return priv->pid > 0;
+}
+
+static gboolean
+ensure_killed (gpointer data)
+{
+	int pid = GPOINTER_TO_INT (data);
+
+	if (kill (pid, 0) == 0)
+		kill (pid, SIGKILL);
+
+	/* ensure the child is reaped */
+	nm_log_dbg (LOGD_SHARING, "waiting for dnsmasq pid %d to exit", pid);
+	waitpid (pid, NULL, 0);
+	nm_log_dbg (LOGD_SHARING, "dnsmasq pid %d cleaned up", pid);
+
+	return FALSE;
+}
+
+void
+nm_dnsmasq_manager_stop (NMDnsMasqManager *manager)
+{
+	NMDnsMasqManagerPrivate *priv;
+
+	g_return_if_fail (NM_IS_DNSMASQ_MANAGER (manager));
+
+	priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
+
+	if (priv->dm_watch_id) {
+		g_source_remove (priv->dm_watch_id);
+		priv->dm_watch_id = 0;
+	}
+
+	if (priv->pid) {
+		if (kill (priv->pid, SIGTERM) == 0)
+			g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
+		else {
+			kill (priv->pid, SIGKILL);
+
+			/* ensure the child is reaped */
+			nm_log_dbg (LOGD_SHARING, "waiting for dnsmasq pid %d to exit", priv->pid);
+			waitpid (priv->pid, NULL, 0);
+			nm_log_dbg (LOGD_SHARING, "dnsmasq pid %d cleaned up", priv->pid);
+		}
+
+		priv->pid = 0;
+	}
+
+	unlink (priv->pidfile);
+}
diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.h b/src/dnsmasq-manager/nm-dnsmasq-manager.h
new file mode 100644
index 00000000..1214d06b
--- /dev/null
+++ b/src/dnsmasq-manager/nm-dnsmasq-manager.h
@@ -0,0 +1,69 @@
+/* -*- 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) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_DNSMASQ_MANAGER_H
+#define NM_DNSMASQ_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-ip4-config.h"
+
+#define NM_TYPE_DNSMASQ_MANAGER            (nm_dnsmasq_manager_get_type ())
+#define NM_DNSMASQ_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManager))
+#define NM_DNSMASQ_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManagerClass))
+#define NM_IS_DNSMASQ_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DNSMASQ_MANAGER))
+#define NM_IS_DNSMASQ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DNSMASQ_MANAGER))
+#define NM_DNSMASQ_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNSMASQ_MANAGER, NMDnsMasqManagerClass))
+
+typedef enum {
+	NM_DNSMASQ_STATUS_UNKNOWN,
+
+	NM_DNSMASQ_STATUS_DEAD,
+	NM_DNSMASQ_STATUS_RUNNING,
+} NMDnsMasqStatus;
+
+typedef struct {
+	GObject parent;
+} NMDnsMasqManager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*state_changed) (NMDnsMasqManager *manager, NMDnsMasqStatus status);
+} NMDnsMasqManagerClass;
+
+GType nm_dnsmasq_manager_get_type (void);
+
+NMDnsMasqManager *nm_dnsmasq_manager_new (const char *iface);
+
+gboolean nm_dnsmasq_manager_start (NMDnsMasqManager *manager,
+                                   NMIP4Config *ip4_config,
+                                   GError **error);
+
+void     nm_dnsmasq_manager_stop  (NMDnsMasqManager *manager);
+
+#define NM_DNSMASQ_MANAGER_ERROR nm_dnsmasq_manager_error_quark()
+#define NM_TYPE_DNSMASQ_MANAGER_ERROR (nm_dnsmasq_manager_error_get_type ()) 
+
+GQuark nm_dnsmasq_manager_error_quark (void);
+
+#endif /* NM_DNSMASQ_MANAGER_H */
diff --git a/src/gdb-cmd b/src/gdb-cmd
new file mode 100644
index 00000000..aea32eb0
--- /dev/null
+++ b/src/gdb-cmd
@@ -0,0 +1,3 @@
+bt
+thread apply all bt full
+q
diff --git a/src/ip6-manager/Makefile.am b/src/ip6-manager/Makefile.am
new file mode 100644
index 00000000..c2f55914
--- /dev/null
+++ b/src/ip6-manager/Makefile.am
@@ -0,0 +1,26 @@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/marshallers \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/named-manager
+
+noinst_LTLIBRARIES = libip6-manager.la
+
+libip6_manager_la_SOURCES = \
+	nm-ip6-manager.c \
+	nm-ip6-manager.h
+
+libip6_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DG_DISABLE_DEPRECATED
+
+libip6_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
diff --git a/src/ip6-manager/Makefile.in b/src/ip6-manager/Makefile.in
new file mode 100644
index 00000000..ddb90e0d
--- /dev/null
+++ b/src/ip6-manager/Makefile.in
@@ -0,0 +1,570 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/ip6-manager
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libip6_manager_la_DEPENDENCIES =  \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libip6_manager_la_OBJECTS = libip6_manager_la-nm-ip6-manager.lo
+libip6_manager_la_OBJECTS = $(am_libip6_manager_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libip6_manager_la_SOURCES)
+DIST_SOURCES = $(libip6_manager_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/marshallers \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/named-manager
+
+noinst_LTLIBRARIES = libip6-manager.la
+libip6_manager_la_SOURCES = \
+	nm-ip6-manager.c \
+	nm-ip6-manager.h
+
+libip6_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DG_DISABLE_DEPRECATED
+
+libip6_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+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/ip6-manager/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/ip6-manager/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libip6-manager.la: $(libip6_manager_la_OBJECTS) $(libip6_manager_la_DEPENDENCIES) 
+	$(LINK)  $(libip6_manager_la_OBJECTS) $(libip6_manager_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libip6_manager_la-nm-ip6-manager.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libip6_manager_la-nm-ip6-manager.lo: nm-ip6-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libip6_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libip6_manager_la-nm-ip6-manager.lo -MD -MP -MF $(DEPDIR)/libip6_manager_la-nm-ip6-manager.Tpo -c -o libip6_manager_la-nm-ip6-manager.lo `test -f 'nm-ip6-manager.c' || echo '$(srcdir)/'`nm-ip6-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libip6_manager_la-nm-ip6-manager.Tpo $(DEPDIR)/libip6_manager_la-nm-ip6-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-ip6-manager.c' object='libip6_manager_la-nm-ip6-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libip6_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libip6_manager_la-nm-ip6-manager.lo `test -f 'nm-ip6-manager.c' || echo '$(srcdir)/'`nm-ip6-manager.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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-noinstLTLIBRARIES \
+	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: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 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/ip6-manager/nm-ip6-manager.c b/src/ip6-manager/nm-ip6-manager.c
new file mode 100644
index 00000000..f6f6127c
--- /dev/null
+++ b/src/ip6-manager/nm-ip6-manager.c
@@ -0,0 +1,1123 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-ip6-manager.c - Handle IPv6 address configuration for 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) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <errno.h>
+#include <netinet/icmp6.h>
+
+#include <netlink/route/addr.h>
+#include <netlink/route/rtnl.h>
+#include <netlink/route/route.h>
+
+#include "nm-ip6-manager.h"
+#include "nm-netlink-monitor.h"
+#include "NetworkManagerUtils.h"
+#include "nm-marshal.h"
+#include "nm-logging.h"
+#include "nm-system.h"
+
+/* Pre-DHCP addrconf timeout, in seconds */
+#define NM_IP6_TIMEOUT 20
+
+/* FIXME? Stolen from the kernel sources */
+#define IF_RA_OTHERCONF 0x80
+#define IF_RA_MANAGED   0x40
+#define IF_RA_RCVD      0x20
+#define IF_RS_SENT      0x10
+
+typedef struct {
+	NMNetlinkMonitor *monitor;
+	GHashTable *devices;
+
+	struct nl_handle *nlh;
+	struct nl_cache *addr_cache, *route_cache;
+
+	guint netlink_id;
+} NMIP6ManagerPrivate;
+
+#define NM_IP6_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_MANAGER, NMIP6ManagerPrivate))
+
+G_DEFINE_TYPE (NMIP6Manager, nm_ip6_manager, G_TYPE_OBJECT)
+
+enum {
+	ADDRCONF_COMPLETE,
+	CONFIG_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef enum {
+	NM_IP6_DEVICE_UNCONFIGURED,
+	NM_IP6_DEVICE_GOT_LINK_LOCAL,
+	NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT,
+	NM_IP6_DEVICE_GOT_ADDRESS,
+	NM_IP6_DEVICE_TIMED_OUT
+} NMIP6DeviceState;
+
+typedef struct {
+	struct in6_addr addr;
+	time_t expires;
+} NMIP6RDNSS;
+
+/******************************************************************/
+
+typedef struct {
+	NMIP6Manager *manager;
+	char *iface;
+	int ifindex;
+
+	char *disable_ip6_path;
+	gboolean disable_ip6_save_valid;
+	guint32 disable_ip6_save;
+
+	guint finish_addrconf_id;
+	guint config_changed_id;
+
+	NMIP6DeviceState state;
+	NMIP6DeviceState target_state;
+	gboolean addrconf_complete;
+
+	GArray *rdnss_servers;
+	guint rdnss_timeout_id;
+
+	guint ip6flags_poll_id;
+
+	guint32 ra_flags;
+} NMIP6Device;
+
+static void
+nm_ip6_device_destroy (NMIP6Device *device)
+{
+	g_return_if_fail (device != NULL);
+
+	/* reset the saved IPv6 value */
+	if (device->disable_ip6_save_valid) {
+		nm_utils_do_sysctl (device->disable_ip6_path,
+		                    device->disable_ip6_save ? "1\n" : "0\n");
+	}
+
+	if (device->finish_addrconf_id)
+		g_source_remove (device->finish_addrconf_id);
+	if (device->config_changed_id)
+		g_source_remove (device->config_changed_id);
+	g_free (device->iface);
+	if (device->rdnss_servers)
+		g_array_free (device->rdnss_servers, TRUE);
+	if (device->rdnss_timeout_id)
+		g_source_remove (device->rdnss_timeout_id);
+	if (device->ip6flags_poll_id)
+		g_source_remove (device->ip6flags_poll_id);
+
+	g_slice_free (NMIP6Device, device);
+}
+
+static NMIP6Device *
+nm_ip6_device_new (NMIP6Manager *manager, int ifindex)
+{
+	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+	NMIP6Device *device;
+
+	g_return_val_if_fail (ifindex > 0, NULL);
+
+	device = g_slice_new0 (NMIP6Device);
+	if (!device) {
+		nm_log_err (LOGD_IP6, "(%d): out of memory creating IP6 addrconf object.",
+		            ifindex);
+		return NULL;
+	}
+
+	device->ifindex = ifindex;
+	device->iface = g_strdup (nm_netlink_index_to_iface (ifindex));
+	if (!device->iface) {
+		nm_log_err (LOGD_IP6, "(%d): could not find interface name from index.",
+		            ifindex);
+		goto error;
+	}
+
+	device->manager = manager;
+
+	device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
+
+	g_hash_table_replace (priv->devices, GINT_TO_POINTER (device->ifindex), device);
+
+	/* and the original value of IPv6 enable/disable */
+	device->disable_ip6_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/disable_ipv6",
+	                                            device->iface);
+	g_assert (device->disable_ip6_path);
+	device->disable_ip6_save_valid = nm_utils_get_proc_sys_net_value (device->disable_ip6_path,
+	                                                                  device->iface,
+	                                                                  &device->disable_ip6_save);
+
+	return device;
+
+error:
+	nm_ip6_device_destroy (device);
+	return NULL;
+}
+
+static NMIP6Device *
+nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex)
+{
+	NMIP6ManagerPrivate *priv;
+
+	g_return_val_if_fail (manager != NULL, NULL);
+	g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);
+
+	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+	return g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex));
+}
+
+/******************************************************************/
+
+typedef struct {
+	NMIP6Device *device;
+	guint dhcp_opts;
+	gboolean success;
+} CallbackInfo;
+
+static gboolean
+finish_addrconf (gpointer user_data)
+{
+	CallbackInfo *info = user_data;
+	NMIP6Device *device = info->device;
+	NMIP6Manager *manager = device->manager;
+	int ifindex;
+
+	device->finish_addrconf_id = 0;
+	device->addrconf_complete = TRUE;
+	ifindex = device->ifindex;
+
+	/* We're done, stop polling IPv6 flags */
+	if (device->ip6flags_poll_id) {
+		g_source_remove (device->ip6flags_poll_id);
+		device->ip6flags_poll_id = 0;
+	}
+
+	/* And tell listeners that addrconf is complete */
+	if (info->success) {
+		g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
+		               ifindex, info->dhcp_opts, TRUE);
+	} else {
+		nm_log_info (LOGD_IP6, "(%s): IP6 addrconf timed out or failed.",
+		             device->iface);
+
+		nm_ip6_manager_cancel_addrconf (manager, ifindex);
+		g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
+		               ifindex, info->dhcp_opts, FALSE);
+	}
+
+	return FALSE;
+}
+
+static gboolean
+emit_config_changed (gpointer user_data)
+{
+	CallbackInfo *info = user_data;
+	NMIP6Device *device = info->device;
+	NMIP6Manager *manager = device->manager;
+
+	device->config_changed_id = 0;
+	g_signal_emit (manager, signals[CONFIG_CHANGED], 0,
+	               device->ifindex,
+	               info->dhcp_opts,
+	               info->success);
+	return FALSE;
+}
+
+static void set_rdnss_timeout (NMIP6Device *device);
+
+static gboolean
+rdnss_expired (gpointer user_data)
+{
+	NMIP6Device *device = user_data;
+	CallbackInfo info = { device, IP6_DHCP_OPT_NONE };
+
+	nm_log_dbg (LOGD_IP6, "(%s): IPv6 RDNSS information expired", device->iface);
+
+	set_rdnss_timeout (device);
+	emit_config_changed (&info);
+	return FALSE;
+}
+
+static void
+set_rdnss_timeout (NMIP6Device *device)
+{
+	time_t expires = 0, now = time (NULL);
+	NMIP6RDNSS *rdnss;
+	int i;
+
+	if (device->rdnss_timeout_id) {
+		g_source_remove (device->rdnss_timeout_id);
+		device->rdnss_timeout_id = 0;
+	}
+
+	/* Find the soonest expiration time. */
+	for (i = 0; i < device->rdnss_servers->len; i++) {
+		rdnss = &g_array_index (device->rdnss_servers, NMIP6RDNSS, i);
+		if (rdnss->expires == 0)
+			continue;
+
+		/* If the entry has already expired, remove it; the "+ 1" is
+		 * because g_timeout_add_seconds() might fudge the timing a
+		 * bit.
+		 */
+		if (rdnss->expires <= now + 1) {
+			char buf[INET6_ADDRSTRLEN + 1];
+
+			if (inet_ntop (AF_INET6, &(rdnss->addr), buf, sizeof (buf)) > 0) {
+				nm_log_dbg (LOGD_IP6, "(%s): removing expired RA-provided nameserver %s",
+				            device->iface, buf);
+			}
+			g_array_remove_index_fast (device->rdnss_servers, i--);
+			continue;
+		}
+
+		if (!expires || rdnss->expires < expires)
+			expires = rdnss->expires;
+	}
+
+	if (expires) {
+		device->rdnss_timeout_id = g_timeout_add_seconds (expires - now,
+														  rdnss_expired,
+														  device);
+	}
+}
+
+static CallbackInfo *
+callback_info_new (NMIP6Device *device, guint dhcp_opts, gboolean success)
+{
+	CallbackInfo *info;
+
+	info = g_malloc0 (sizeof (CallbackInfo));
+	info->device = device;
+	info->dhcp_opts = dhcp_opts;
+	info->success = success;
+	return info;
+}
+
+static const char *
+state_to_string (NMIP6DeviceState state)
+{
+	switch (state) {
+	case NM_IP6_DEVICE_UNCONFIGURED:
+		return "unconfigured";
+	case NM_IP6_DEVICE_GOT_LINK_LOCAL:
+		return "got-link-local";
+	case NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT:
+		return "got-ra";
+	case NM_IP6_DEVICE_GOT_ADDRESS:
+		return "got-address";
+	case NM_IP6_DEVICE_TIMED_OUT:
+		return "timed-out";
+	default:
+		return "unknown";
+	}
+}
+
+static void
+nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed)
+{
+	NMIP6Manager *manager = device->manager;
+	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+	struct rtnl_addr *rtnladdr;
+	struct nl_addr *nladdr;
+	struct in6_addr *addr;
+	CallbackInfo *info;
+	guint dhcp_opts = IP6_DHCP_OPT_NONE;
+	gboolean found_linklocal = FALSE, found_other = FALSE;
+
+	nm_log_dbg (LOGD_IP6, "(%s): syncing with netlink (ra_flags 0x%X) (state/target '%s'/'%s')",
+	            device->iface, device->ra_flags,
+	            state_to_string (device->state),
+	            state_to_string (device->target_state));
+
+	/* Look for any IPv6 addresses the kernel may have set for the device */
+	for (rtnladdr = (struct rtnl_addr *) nl_cache_get_first (priv->addr_cache);
+		 rtnladdr;
+		 rtnladdr = (struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) rtnladdr)) {
+		char buf[INET6_ADDRSTRLEN];
+
+		if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex)
+			continue;
+
+		nladdr = rtnl_addr_get_local (rtnladdr);
+		if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
+			continue;
+
+		addr = nl_addr_get_binary_addr (nladdr);
+
+		if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0) {
+			nm_log_dbg (LOGD_IP6, "(%s): netlink address: %s",
+			            device->iface, buf);
+		}
+
+		if (IN6_IS_ADDR_LINKLOCAL (addr)) {
+			if (device->state == NM_IP6_DEVICE_UNCONFIGURED)
+				device->state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
+			found_linklocal = TRUE;
+		} else {
+			if (device->state < NM_IP6_DEVICE_GOT_ADDRESS)
+				device->state = NM_IP6_DEVICE_GOT_ADDRESS;
+			found_other = TRUE;
+		}
+	}
+
+	/* There might be a LL address hanging around on the interface from
+	 * before in the initial run, but if it goes away later, make sure we
+	 * regress from GOT_LINK_LOCAL back to UNCONFIGURED.
+	 */
+	if ((device->state == NM_IP6_DEVICE_GOT_LINK_LOCAL) && !found_linklocal)
+		device->state = NM_IP6_DEVICE_UNCONFIGURED;
+
+	nm_log_dbg (LOGD_IP6, "(%s): addresses synced (state %s)",
+	            device->iface, state_to_string (device->state));
+
+	/* We only care about router advertisements if we want a real IPv6 address */
+	if (   (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS)
+	    && (device->ra_flags & IF_RA_RCVD)) {
+
+		if (device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT)
+			device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT;
+
+		if (device->ra_flags & IF_RA_MANAGED) {
+			dhcp_opts = IP6_DHCP_OPT_MANAGED;
+			nm_log_dbg (LOGD_IP6, "router advertisement deferred to DHCPv6");
+		} else if (device->ra_flags & IF_RA_OTHERCONF) {
+			dhcp_opts = IP6_DHCP_OPT_OTHERCONF;
+			nm_log_dbg (LOGD_IP6, "router advertisement requests parallel DHCPv6");
+		}
+	}
+
+	if (!device->addrconf_complete) {
+		/* Managed mode (ie DHCP only) short-circuits automatic addrconf, so
+		 * we don't bother waiting for the device's target state to be reached
+		 * when the RA requests managed mode.
+		 */
+		if (   (device->state >= device->target_state)
+		    || (dhcp_opts == IP6_DHCP_OPT_MANAGED)) {
+			/* device->finish_addrconf_id may currently be a timeout
+			 * rather than an idle, so we remove the existing source.
+			 */
+			if (device->finish_addrconf_id)
+				g_source_remove (device->finish_addrconf_id);
+
+			nm_log_dbg (LOGD_IP6, "(%s): reached target state or Managed-mode requested (state '%s') (dhcp opts 0x%X)",
+			            device->iface, state_to_string (device->state),
+			            dhcp_opts);
+
+			info = callback_info_new (device, dhcp_opts, TRUE);
+			device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+			                                              finish_addrconf,
+			                                              info,
+			                                              (GDestroyNotify) g_free);
+		}
+	} else if (config_changed) {
+		if (!device->config_changed_id) {
+			gboolean success = TRUE;
+
+			/* If for some reason an RA-provided address disappeared, we need
+			 * to make sure we fail the connection as it's no longer valid.
+			 */
+			if (   (device->state == NM_IP6_DEVICE_GOT_ADDRESS)
+			    && (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS)
+			    && !found_other) {
+				nm_log_dbg (LOGD_IP6, "(%s): RA-provided address no longer valid",
+				            device->iface);
+				success = FALSE;
+			}
+
+			info = callback_info_new (device, dhcp_opts, success);
+			device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+			                                             emit_config_changed,
+			                                             info,
+			                                             (GDestroyNotify) g_free);
+		}
+	}
+}
+
+static void
+ref_object (struct nl_object *obj, void *data)
+{
+	struct nl_object **out = data;
+
+	nl_object_get (obj);
+	*out = obj;
+}
+
+static NMIP6Device *
+process_addr (NMIP6Manager *manager, struct nl_msg *msg)
+{
+	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+	NMIP6Device *device;
+	struct rtnl_addr *rtnladdr;
+	int old_size;
+
+	nm_log_dbg (LOGD_IP6, "processing netlink new/del address message");
+
+	rtnladdr = NULL;
+	nl_msg_parse (msg, ref_object, &rtnladdr);
+	if (!rtnladdr) {
+		nm_log_dbg (LOGD_IP6, "error processing netlink new/del address message");
+		return NULL;
+	}
+
+	device = nm_ip6_manager_get_device (manager, rtnl_addr_get_ifindex (rtnladdr));
+	if (!device) {
+		nm_log_dbg (LOGD_IP6, "ignoring message for unknown device");
+		return NULL;
+	}
+
+	old_size = nl_cache_nitems (priv->addr_cache);
+	nl_cache_include (priv->addr_cache, (struct nl_object *)rtnladdr, NULL);
+	rtnl_addr_put (rtnladdr);
+
+	/* The kernel will re-notify us of automatically-added addresses
+	 * every time it gets another router advertisement. We only want
+	 * to notify higher levels if we actually changed something.
+	 */
+	if (nl_cache_nitems (priv->addr_cache) == old_size) {
+		nm_log_dbg (LOGD_IP6, "(%s): address cache unchanged, ignoring message",
+		            device->iface);
+		return NULL;
+	}
+
+	return device;
+}
+
+static NMIP6Device *
+process_route (NMIP6Manager *manager, struct nl_msg *msg)
+{
+	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+	NMIP6Device *device;
+	struct rtnl_route *rtnlroute;
+	int old_size;
+
+	nm_log_dbg (LOGD_IP6, "processing netlink new/del route message");
+
+	rtnlroute = NULL;
+	nl_msg_parse (msg, ref_object, &rtnlroute);
+	if (!rtnlroute) {
+		nm_log_dbg (LOGD_IP6, "error processing netlink new/del route message");
+		return NULL;
+	}
+
+	device = nm_ip6_manager_get_device (manager, rtnl_route_get_oif (rtnlroute));
+	if (!device) {
+		nm_log_dbg (LOGD_IP6, "ignoring message for unknown device");
+		return NULL;
+	}
+
+	old_size = nl_cache_nitems (priv->route_cache);
+	nl_cache_include (priv->route_cache, (struct nl_object *)rtnlroute, NULL);
+	rtnl_route_put (rtnlroute);
+
+	/* As above in process_addr */
+	if (nl_cache_nitems (priv->route_cache) == old_size) {
+		nm_log_dbg (LOGD_IP6, "(%s): route cache unchanged, ignoring message",
+		            device->iface);
+		return NULL;
+	}
+
+	return device;
+}
+
+static NMIP6Device *
+process_prefix (NMIP6Manager *manager, struct nl_msg *msg)
+{
+	struct prefixmsg *pmsg;
+	NMIP6Device *device;
+
+	/* We don't care about the prefix itself, but if we receive a
+	 * router advertisement telling us to use DHCP, we might not
+	 * get any RTM_NEWADDRs or RTM_NEWROUTEs, so this is our only
+	 * way to notice immediately that an RA was received.
+	 */
+
+	nm_log_dbg (LOGD_IP6, "processing netlink new prefix message");
+
+	pmsg = (struct prefixmsg *) NLMSG_DATA (nlmsg_hdr (msg));
+	device = nm_ip6_manager_get_device (manager, pmsg->prefix_ifindex);
+
+	if (!device || device->addrconf_complete) {
+		nm_log_dbg (LOGD_IP6, "(%s): ignoring unknown or completed device",
+		            device ? device->iface : "(none)");
+		return NULL;
+	}
+
+	return device;
+}
+
+/* RDNSS parsing code based on rdnssd, Copyright 2007 Pierre Ynard,
+ * Rémi Denis-Courmont. GPLv2/3
+ */
+
+#define ND_OPT_RDNSS 25
+struct nd_opt_rdnss {
+	uint8_t nd_opt_rdnss_type;
+	uint8_t nd_opt_rdnss_len;
+	uint16_t nd_opt_rdnss_reserved1;
+	uint32_t nd_opt_rdnss_lifetime;
+	/* followed by one or more IPv6 addresses */
+};
+
+static NMIP6Device *
+process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg)
+{
+	NMIP6Device *device;
+	struct nduseroptmsg *ndmsg;
+	struct nd_opt_hdr *opt;
+	guint opts_len, i;
+	time_t now = time (NULL);
+	struct nd_opt_rdnss *rdnss_opt;
+	struct in6_addr *addr;
+	GArray *servers;
+	NMIP6RDNSS server, *sa, *sb;
+	gboolean changed;
+
+	nm_log_dbg (LOGD_IP6, "processing netlink nduseropt message");
+
+	ndmsg = (struct nduseroptmsg *) NLMSG_DATA (nlmsg_hdr (msg));
+
+	if (ndmsg->nduseropt_family != AF_INET6 ||
+		ndmsg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
+		ndmsg->nduseropt_icmp_code != 0) {
+		nm_log_dbg (LOGD_IP6, "ignoring non-Router Advertisement message");
+		return NULL;
+	}
+
+	device = nm_ip6_manager_get_device (manager, ndmsg->nduseropt_ifindex);
+	if (!device) {
+		nm_log_dbg (LOGD_IP6, "ignoring message for unknown device");
+		return NULL;
+	}
+
+	servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
+
+	opt = (struct nd_opt_hdr *) (ndmsg + 1);
+	opts_len = ndmsg->nduseropt_opts_len;
+
+	while (opts_len >= sizeof (struct nd_opt_hdr)) {
+		size_t nd_opt_len = opt->nd_opt_len;
+
+		if (nd_opt_len == 0 || opts_len < (nd_opt_len << 3))
+			break;
+
+		if (opt->nd_opt_type != ND_OPT_RDNSS)
+			goto next;
+
+		if (nd_opt_len < 3 || (nd_opt_len & 1) == 0)
+			goto next;
+
+		rdnss_opt = (struct nd_opt_rdnss *) opt;
+
+		/* Pad the DNS server expiry somewhat to give a bit of slack in cases
+		 * where one RA gets lost or something (which can happen on unreliable
+		 * links like wifi where certain types of frames are not retransmitted).
+		 */
+		server.expires = now + ntohl (rdnss_opt->nd_opt_rdnss_lifetime) + 10;
+
+		for (addr = (struct in6_addr *) (rdnss_opt + 1); nd_opt_len >= 2; addr++, nd_opt_len -= 2) {
+			char buf[INET6_ADDRSTRLEN + 1];
+
+			if (inet_ntop (AF_INET6, addr, buf, sizeof (buf))) {
+				nm_log_dbg (LOGD_IP6, "(%s): found RA-provided nameserver %s (expires in %d seconds)",
+				            device->iface, buf,
+				            ntohl (rdnss_opt->nd_opt_rdnss_lifetime));
+			}
+
+			server.addr = *addr;
+			g_array_append_val (servers, server);
+		}
+
+	next:
+		opts_len -= opt->nd_opt_len << 3;
+		opt = (struct nd_opt_hdr *) ((uint8_t *) opt + (opt->nd_opt_len << 3));
+	}
+
+	/* See if anything (other than expiration time) changed */
+	if (servers->len != device->rdnss_servers->len)
+		changed = TRUE;
+	else {
+		for (i = 0; i < servers->len; i++) {
+			sa = &(g_array_index (servers, NMIP6RDNSS, i));
+			sb = &(g_array_index (device->rdnss_servers, NMIP6RDNSS, i));
+			if (IN6_ARE_ADDR_EQUAL (&sa->addr, &sb->addr) == FALSE) {
+				changed = TRUE;
+				break;
+			}
+		}
+		changed = FALSE;
+	}
+
+	if (changed) {
+		nm_log_dbg (LOGD_IP6, "(%s): RA-provided nameservers changed", device->iface);
+	}
+
+	/* Always copy in new servers (even if unchanged) to get their updated
+	 * expiration times.
+	 */
+	g_array_free (device->rdnss_servers, TRUE);
+	device->rdnss_servers = servers;
+
+	/* Timeouts may have changed even if IPs didn't */
+	set_rdnss_timeout (device);
+
+	if (changed)
+		return device;
+	else
+		return NULL;
+}
+
+static struct nla_policy link_policy[IFLA_MAX + 1] = {
+	[IFLA_PROTINFO] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy link_prot_policy[IFLA_INET6_MAX + 1] = {
+	[IFLA_INET6_FLAGS]	= { .type = NLA_U32 },
+};
+
+static NMIP6Device *
+process_newlink (NMIP6Manager *manager, struct nl_msg *msg)
+{
+	struct nlmsghdr *hdr = nlmsg_hdr (msg);
+	struct ifinfomsg *ifi;
+	NMIP6Device *device;
+	struct nlattr *tb[IFLA_MAX + 1];
+	struct nlattr *pi[IFLA_INET6_MAX + 1];
+	int err;
+
+	ifi = nlmsg_data (hdr);
+	if (ifi->ifi_family != AF_INET6) {
+		nm_log_dbg (LOGD_IP6, "ignoring netlink message family %d", ifi->ifi_family);
+		return NULL;
+	}
+
+	device = nm_ip6_manager_get_device (manager, ifi->ifi_index);
+	if (!device || device->addrconf_complete) {
+		nm_log_dbg (LOGD_IP6, "(%s): ignoring unknown or completed device",
+		            device ? device->iface : "(none)");
+		return NULL;
+	}
+
+	/* FIXME: we have to do this manually for now since libnl doesn't yet
+	 * support the IFLA_PROTINFO attribute of NEWLINK messages.  When it does,
+	 * we can get rid of this function and just grab IFLA_PROTINFO from
+	 * nm_ip6_device_sync_from_netlink(), then get the IFLA_INET6_FLAGS out of
+	 * the PROTINFO.
+	 */
+
+	err = nlmsg_parse (hdr, sizeof (*ifi), tb, IFLA_MAX, link_policy);
+	if (err < 0) {
+		nm_log_dbg (LOGD_IP6, "(%s): error parsing PROTINFO attribute", device->iface);
+		return NULL;
+	}
+	if (!tb[IFLA_PROTINFO]) {
+		nm_log_dbg (LOGD_IP6, "(%s): message had no PROTINFO attribute", device->iface);
+		return NULL;
+	}
+
+	err = nla_parse_nested (pi, IFLA_INET6_MAX, tb[IFLA_PROTINFO], link_prot_policy);
+	if (err < 0) {
+		nm_log_dbg (LOGD_IP6, "(%s): error parsing PROTINFO flags", device->iface);
+		return NULL;
+	}
+	if (!pi[IFLA_INET6_FLAGS]) {
+		nm_log_dbg (LOGD_IP6, "(%s): message had no PROTINFO flags", device->iface);
+		return NULL;
+	}
+
+	device->ra_flags = nla_get_u32 (pi[IFLA_INET6_FLAGS]);
+	nm_log_dbg (LOGD_IP6, "(%s): got IPv6 flags 0x%X", device->iface, device->ra_flags);
+
+	return device;
+}
+
+static void
+netlink_notification (NMNetlinkMonitor *monitor, struct nl_msg *msg, gpointer user_data)
+{
+	NMIP6Manager *manager = (NMIP6Manager *) user_data;
+	NMIP6Device *device;
+	struct nlmsghdr *hdr;
+	gboolean config_changed = FALSE;
+
+	hdr = nlmsg_hdr (msg);
+	nm_log_dbg (LOGD_HW, "netlink notificate type %d", hdr->nlmsg_type);
+	switch (hdr->nlmsg_type) {
+	case RTM_NEWADDR:
+	case RTM_DELADDR:
+		device = process_addr (manager, msg);
+		config_changed = TRUE;
+		break;
+	case RTM_NEWROUTE:
+	case RTM_DELROUTE:
+		device = process_route (manager, msg);
+		config_changed = TRUE;
+		break;
+	case RTM_NEWPREFIX:
+		device = process_prefix (manager, msg);
+		break;
+	case RTM_NEWNDUSEROPT:
+		device = process_nduseropt (manager, msg);
+		config_changed = TRUE;
+		break;
+	case RTM_NEWLINK:
+		device = process_newlink (manager, msg);
+		config_changed = TRUE;
+		break;
+	default:
+		return;
+	}
+
+	if (device) {
+		nm_log_dbg (LOGD_IP6, "(%s): syncing device with netlink changes", device->iface);
+		nm_ip6_device_sync_from_netlink (device, config_changed);
+	}
+}
+
+void
+nm_ip6_manager_prepare_interface (NMIP6Manager *manager,
+                                  int ifindex,
+                                  NMSettingIP6Config *s_ip6,
+                                  const char *accept_ra_path)
+{
+	NMIP6ManagerPrivate *priv;
+	NMIP6Device *device;
+	const char *method = NULL;
+
+	g_return_if_fail (NM_IS_IP6_MANAGER (manager));
+	g_return_if_fail (ifindex > 0);
+
+	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+
+	device = nm_ip6_device_new (manager, ifindex);
+	g_return_if_fail (device != NULL);
+	g_return_if_fail (   strchr (device->iface, '/') == NULL
+	                  && strcmp (device->iface, "all") != 0
+	                  && strcmp (device->iface, "default") != 0);
+
+	if (s_ip6)
+		method = nm_setting_ip6_config_get_method (s_ip6);
+	if (!method)
+		method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+
+	/* Establish target state and turn router advertisement acceptance on or off */
+	if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
+		device->target_state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
+		nm_utils_do_sysctl (accept_ra_path, "0\n");
+	} else {
+		device->target_state = NM_IP6_DEVICE_GOT_ADDRESS;
+		nm_utils_do_sysctl (accept_ra_path, "1\n");
+	}
+}
+
+static gboolean
+poll_ip6_flags (gpointer user_data)
+{
+	nm_netlink_monitor_request_ip6_info (NM_NETLINK_MONITOR (user_data), NULL);
+	return TRUE;
+}
+
+void
+nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, int ifindex)
+{
+	NMIP6ManagerPrivate *priv;
+	NMIP6Device *device;
+	CallbackInfo *info;
+
+	g_return_if_fail (NM_IS_IP6_MANAGER (manager));
+	g_return_if_fail (ifindex > 0);
+
+	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+
+	device = (NMIP6Device *) g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex));
+	g_return_if_fail (device != NULL);
+
+	nm_log_info (LOGD_IP6, "Activation (%s) Beginning IP6 addrconf.", device->iface);
+
+	device->addrconf_complete = FALSE;
+	device->ra_flags = 0;
+
+	/* Set up a timeout on the transaction to kill it after the timeout */
+	info = callback_info_new (device, 0, FALSE);
+	device->finish_addrconf_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+	                                                         NM_IP6_TIMEOUT,
+	                                                         finish_addrconf,
+	                                                         info,
+	                                                         (GDestroyNotify) g_free);
+
+	/* Bounce IPv6 on the interface to ensure the kernel will start looking for
+	 * new RAs; there doesn't seem to be a better way to do this right now.
+	 */
+	if (device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS) {
+		nm_utils_do_sysctl (device->disable_ip6_path, "1\n");
+		g_usleep (200);
+		nm_utils_do_sysctl (device->disable_ip6_path, "0\n");
+	}
+
+	device->ip6flags_poll_id = g_timeout_add_seconds (1, poll_ip6_flags, priv->monitor);
+
+	/* Kick off the initial IPv6 flags request */
+	nm_netlink_monitor_request_ip6_info (priv->monitor, NULL);
+
+	/* Sync flags, etc, from netlink; this will also notice if the
+	 * device is already fully configured and schedule the
+	 * ADDRCONF_COMPLETE signal in that case.
+	 */
+	nm_ip6_device_sync_from_netlink (device, FALSE);
+}
+
+void
+nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, int ifindex)
+{
+	g_return_if_fail (NM_IS_IP6_MANAGER (manager));
+	g_return_if_fail (ifindex > 0);
+
+	g_hash_table_remove (NM_IP6_MANAGER_GET_PRIVATE (manager)->devices,
+	                     GINT_TO_POINTER (ifindex));
+}
+
+#define FIRST_ROUTE(m) ((struct rtnl_route *) nl_cache_get_first (m))
+#define NEXT_ROUTE(m) ((struct rtnl_route *) nl_cache_get_next ((struct nl_object *) m))
+
+#define FIRST_ADDR(m) ((struct rtnl_addr *) nl_cache_get_first (m))
+#define NEXT_ADDR(m) ((struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) m))
+
+NMIP6Config *
+nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex)
+{
+	NMIP6ManagerPrivate *priv;
+	NMIP6Device *device;
+	NMIP6Config *config;
+	struct rtnl_addr *rtnladdr;
+	struct nl_addr *nladdr;
+	struct in6_addr *addr;
+	NMIP6Address *ip6addr;
+	struct rtnl_route *rtnlroute;
+	struct nl_addr *nldest, *nlgateway;
+	struct in6_addr *dest, *gateway;
+	gboolean defgw_set = FALSE;
+	struct in6_addr defgw;
+	uint32_t metric;
+	NMIP6Route *ip6route;
+	int i;
+
+	g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);
+	g_return_val_if_fail (ifindex > 0, NULL);
+
+	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+
+	device = (NMIP6Device *) g_hash_table_lookup (priv->devices,
+	                                              GINT_TO_POINTER (ifindex));
+	if (!device) {
+		nm_log_warn (LOGD_IP6, "(%d): addrconf not started.", ifindex);
+		return NULL;
+	}
+
+	config = nm_ip6_config_new ();
+	if (!config) {
+		nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 config object.",
+		            device->iface);
+		return NULL;
+	}
+
+	/* Make sure we refill the route and address caches, otherwise we won't get
+	 * up-to-date information here since the netlink route/addr change messages
+	 * may be lagging a bit.
+	 */
+	nl_cache_refill (priv->nlh, priv->route_cache);
+	nl_cache_refill (priv->nlh, priv->addr_cache);
+
+	/* Add routes */
+	for (rtnlroute = FIRST_ROUTE (priv->route_cache); rtnlroute; rtnlroute = NEXT_ROUTE (rtnlroute)) {
+		/* Make sure it's an IPv6 route for this device */
+		if (rtnl_route_get_oif (rtnlroute) != device->ifindex)
+			continue;
+		if (rtnl_route_get_family (rtnlroute) != AF_INET6)
+			continue;
+
+		nldest = rtnl_route_get_dst (rtnlroute);
+		if (!nldest || nl_addr_get_family (nldest) != AF_INET6)
+			continue;
+		dest = nl_addr_get_binary_addr (nldest);
+
+		nlgateway = rtnl_route_get_gateway (rtnlroute);
+		if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6)
+			continue;
+		gateway = nl_addr_get_binary_addr (nlgateway);
+
+		if (rtnl_route_get_dst_len (rtnlroute) == 0) {
+			/* Default gateway route; don't add to normal routes but to each address */
+			if (!defgw_set) {
+				memcpy (&defgw, gateway, sizeof (defgw));
+				defgw_set = TRUE;
+			}
+			continue;
+		}
+
+		/* Also ignore link-local routes where the destination and gateway are
+		 * the same, which apparently get added by the kernel but return -EINVAL
+		 * when we try to add them via netlink.
+		 */
+		if (gateway && IN6_ARE_ADDR_EQUAL (dest, gateway))
+			continue;
+
+		ip6route = nm_ip6_route_new ();
+		nm_ip6_route_set_dest (ip6route, dest);
+		nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute));
+		nm_ip6_route_set_next_hop (ip6route, gateway);
+		metric = rtnl_route_get_metric (rtnlroute, 1);
+		if (metric != UINT_MAX)
+			nm_ip6_route_set_metric (ip6route, metric);
+		nm_ip6_config_take_route (config, ip6route);
+	}
+
+	/* Add addresses */
+	for (rtnladdr = FIRST_ADDR (priv->addr_cache); rtnladdr; rtnladdr = NEXT_ADDR (rtnladdr)) {
+		if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex)
+			continue;
+
+		nladdr = rtnl_addr_get_local (rtnladdr);
+		if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
+			continue;
+
+		addr = nl_addr_get_binary_addr (nladdr);
+		ip6addr = nm_ip6_address_new ();
+		nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr));
+		nm_ip6_address_set_address (ip6addr, addr);
+		nm_ip6_config_take_address (config, ip6addr);
+		if (defgw_set)
+			nm_ip6_address_set_gateway (ip6addr, &defgw);
+	}
+
+	/* Add DNS servers */
+	if (device->rdnss_servers) {
+		NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data);
+
+		for (i = 0; i < device->rdnss_servers->len; i++)
+			nm_ip6_config_add_nameserver (config, &rdnss[i].addr);
+	}
+
+	return config;
+}
+
+/******************************************************************/
+
+static NMIP6Manager *
+nm_ip6_manager_new (void)
+{
+	NMIP6Manager *manager;
+	NMIP6ManagerPrivate *priv;
+
+	manager = g_object_new (NM_TYPE_IP6_MANAGER, NULL);
+	priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+
+	if (!priv->devices) {
+		nm_log_err (LOGD_IP6, "not enough memory to initialize IP6 manager tables");
+		g_object_unref (manager);
+		manager = NULL;
+	}
+
+	return manager;
+}
+
+static NMIP6Manager *singleton = NULL;
+
+NMIP6Manager *
+nm_ip6_manager_get (void)
+{
+	if (!singleton) {
+		singleton = nm_ip6_manager_new ();
+		g_assert (singleton);
+	} else
+		g_object_ref (singleton);
+
+	return singleton;
+}
+
+static void
+nm_ip6_manager_init (NMIP6Manager *manager)
+{
+	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
+
+	priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+	                                       NULL,
+	                                       (GDestroyNotify) nm_ip6_device_destroy);
+
+	priv->monitor = nm_netlink_monitor_get ();
+	nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_IFADDR, NULL);
+	nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_PREFIX, NULL);
+	nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_ND_USEROPT, NULL);
+	nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_LINK, NULL);
+
+	priv->netlink_id = g_signal_connect (priv->monitor, "notification",
+	                                     G_CALLBACK (netlink_notification), manager);
+
+	priv->nlh = nm_netlink_get_default_handle ();
+	priv->addr_cache = rtnl_addr_alloc_cache (priv->nlh);
+	priv->route_cache = rtnl_route_alloc_cache (priv->nlh);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (object);
+
+	g_signal_handler_disconnect (priv->monitor, priv->netlink_id);
+
+	g_hash_table_destroy (priv->devices);
+	g_object_unref (priv->monitor);
+	nl_cache_free (priv->addr_cache);
+	nl_cache_free (priv->route_cache);
+
+	singleton = NULL;
+
+	G_OBJECT_CLASS (nm_ip6_manager_parent_class)->finalize (object);
+}
+
+static void
+nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+	g_type_class_add_private (manager_class, sizeof (NMIP6ManagerPrivate));
+
+	/* virtual methods */
+	object_class->finalize = finalize;
+
+	/* signals */
+	signals[ADDRCONF_COMPLETE] =
+		g_signal_new ("addrconf-complete",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete),
+					  NULL, NULL,
+					  _nm_marshal_VOID__INT_UINT_BOOLEAN,
+					  G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_BOOLEAN);
+
+	signals[CONFIG_CHANGED] =
+		g_signal_new ("config-changed",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed),
+					  NULL, NULL,
+					  _nm_marshal_VOID__INT_UINT_BOOLEAN,
+					  G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_BOOLEAN);
+}
+
diff --git a/src/ip6-manager/nm-ip6-manager.h b/src/ip6-manager/nm-ip6-manager.h
new file mode 100644
index 00000000..77c11068
--- /dev/null
+++ b/src/ip6-manager/nm-ip6-manager.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-ip6-manager.c - Handle IPv6 address configuration for 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) 2009 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_IP6_MANAGER_H
+#define NM_IP6_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <nm-setting-ip6-config.h>
+
+#include "nm-ip6-config.h"
+
+#define NM_TYPE_IP6_MANAGER            (nm_ip6_manager_get_type ())
+#define NM_IP6_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP6_MANAGER, NMIP6Manager))
+#define NM_IP6_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP6_MANAGER, NMIP6ManagerClass))
+#define NM_IS_IP6_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IP6_MANAGER))
+#define NM_IS_IP6_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IP6_MANAGER))
+#define NM_IP6_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP6_MANAGER, NMIP6ManagerClass))
+
+enum {
+	IP6_DHCP_OPT_NONE = 0,
+	IP6_DHCP_OPT_OTHERCONF,
+	IP6_DHCP_OPT_MANAGED
+};
+
+typedef struct {
+	GObject parent;
+} NMIP6Manager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+
+	/* addrconf_complete is emitted only during initial configuration to indicate
+	 * that the initial configuration is complete.
+	 */
+	void (*addrconf_complete) (NMIP6Manager *manager,
+	                           guint32 ifindex,
+	                           guint dhcp_opts,
+	                           gboolean success);
+
+	/* config_changed gets emitted only *after* initial configuration is
+	 * complete; it's like DHCP renew and indicates that the existing config
+	 * of the interface has changed.
+	 */
+	void (*config_changed)    (NMIP6Manager *manager,
+	                           guint32 ifindex,
+	                           guint dhcp_opts,
+	                           gboolean success);
+} NMIP6ManagerClass;
+
+GType nm_ip6_manager_get_type (void);
+
+NMIP6Manager *nm_ip6_manager_get               (void);
+void          nm_ip6_manager_prepare_interface (NMIP6Manager *manager,
+                                                int ifindex,
+                                                NMSettingIP6Config *s_ip6,
+                                                const char *accept_ra_path);
+void          nm_ip6_manager_begin_addrconf    (NMIP6Manager *manager,
+                                                int ifindex);
+void          nm_ip6_manager_cancel_addrconf   (NMIP6Manager *manager,
+                                                int ifindex);
+
+NMIP6Config * nm_ip6_manager_get_ip6_config    (NMIP6Manager *manager,
+                                                int ifindex);
+
+#endif /* NM_IP6_MANAGER_H */
diff --git a/src/logging/Makefile.am b/src/logging/Makefile.am
new file mode 100644
index 00000000..51a6b25d
--- /dev/null
+++ b/src/logging/Makefile.am
@@ -0,0 +1,15 @@
+noinst_LTLIBRARIES = libnm-logging.la
+
+libnm_logging_la_SOURCES = \
+	nm-logging.c \
+	nm-logging.h
+
+libnm_logging_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DG_DISABLE_DEPRECATED
+
+libnm_logging_la_LIBADD = \
+	-ldl \
+	$(GLIB_LIBS)
+
diff --git a/src/logging/Makefile.in b/src/logging/Makefile.in
new file mode 100644
index 00000000..8cbe2922
--- /dev/null
+++ b/src/logging/Makefile.in
@@ -0,0 +1,556 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/logging
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libnm_logging_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am_libnm_logging_la_OBJECTS = libnm_logging_la-nm-logging.lo
+libnm_logging_la_OBJECTS = $(am_libnm_logging_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libnm_logging_la_SOURCES)
+DIST_SOURCES = $(libnm_logging_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libnm-logging.la
+libnm_logging_la_SOURCES = \
+	nm-logging.c \
+	nm-logging.h
+
+libnm_logging_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DG_DISABLE_DEPRECATED
+
+libnm_logging_la_LIBADD = \
+	-ldl \
+	$(GLIB_LIBS)
+
+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/logging/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/logging/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libnm-logging.la: $(libnm_logging_la_OBJECTS) $(libnm_logging_la_DEPENDENCIES) 
+	$(LINK)  $(libnm_logging_la_OBJECTS) $(libnm_logging_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_logging_la-nm-logging.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libnm_logging_la-nm-logging.lo: nm-logging.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_logging_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_logging_la-nm-logging.lo -MD -MP -MF $(DEPDIR)/libnm_logging_la-nm-logging.Tpo -c -o libnm_logging_la-nm-logging.lo `test -f 'nm-logging.c' || echo '$(srcdir)/'`nm-logging.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_logging_la-nm-logging.Tpo $(DEPDIR)/libnm_logging_la-nm-logging.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-logging.c' object='libnm_logging_la-nm-logging.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_logging_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_logging_la-nm-logging.lo `test -f 'nm-logging.c' || echo '$(srcdir)/'`nm-logging.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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-noinstLTLIBRARIES \
+	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: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 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/logging/nm-logging.c b/src/logging/nm-logging.c
new file mode 100644
index 00000000..9d1905da
--- /dev/null
+++ b/src/logging/nm-logging.c
@@ -0,0 +1,377 @@
+/* -*- 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) 2006 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#include <syslog.h>
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <execinfo.h>
+#include <strings.h>
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include "nm-logging.h"
+
+static guint32 log_level = LOGL_INFO | LOGL_WARN | LOGL_ERR;
+static guint32 log_domains = \
+	LOGD_HW | LOGD_RFKILL | LOGD_ETHER | LOGD_WIFI | LOGD_BT | LOGD_MB | \
+	LOGD_DHCP4 | LOGD_DHCP6 | LOGD_PPP | LOGD_IP4 | LOGD_IP6 | LOGD_AUTOIP4 | \
+	LOGD_DNS | LOGD_VPN | LOGD_SHARING | LOGD_SUPPLICANT | LOGD_USER_SET | \
+	LOGD_SYS_SET | LOGD_SUSPEND | LOGD_CORE | LOGD_DEVICE | LOGD_OLPC_MESH;
+
+typedef struct {
+	guint32 num;
+	const char *name;
+} LogDesc;
+
+static const LogDesc level_descs[] = {
+	{ LOGL_ERR, "ERR" },
+	{ LOGL_WARN | LOGL_ERR, "WARN" },
+	{ LOGL_INFO | LOGL_WARN | LOGL_ERR, "INFO" },
+	{ LOGL_DEBUG | LOGL_INFO | LOGL_WARN | LOGL_ERR, "DEBUG" },
+	{ 0, NULL }
+};
+
+static const LogDesc domain_descs[] = {
+	{ LOGD_NONE,      "NONE" },
+	{ LOGD_HW,        "HW" },
+	{ LOGD_RFKILL,    "RFKILL" },
+	{ LOGD_ETHER,     "ETHER" },
+	{ LOGD_WIFI,      "WIFI" },
+	{ LOGD_BT,        "BT" },
+	{ LOGD_MB,        "MB" },
+	{ LOGD_DHCP4,     "DHCP4" },
+	{ LOGD_DHCP6,     "DHCP6" },
+	{ LOGD_PPP,       "PPP" },
+	{ LOGD_WIFI_SCAN, "WIFI_SCAN" },
+	{ LOGD_IP4,       "IP4" },
+	{ LOGD_IP6,       "IP6" },
+	{ LOGD_AUTOIP4,   "AUTOIP4" },
+	{ LOGD_DNS,       "DNS" },
+	{ LOGD_VPN,       "VPN" },
+	{ LOGD_SHARING,   "SHARING" },
+	{ LOGD_SUPPLICANT,"SUPPLICANT" },
+	{ LOGD_USER_SET,  "USER_SET" },
+	{ LOGD_SYS_SET,   "SYS_SET" },
+	{ LOGD_SUSPEND,   "SUSPEND" },
+	{ LOGD_CORE,      "CORE" },
+	{ LOGD_DEVICE,    "DEVICE" },
+	{ LOGD_OLPC_MESH, "OLPC" },
+	{ 0, NULL }
+};
+
+/************************************************************************/
+
+enum {
+    NM_LOGGING_ERROR_UNKNOWN_LEVEL = 0,
+    NM_LOGGING_ERROR_UNKNOWN_DOMAIN = 1,
+};
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GQuark
+nm_logging_error_quark (void)
+{
+    static GQuark ret = 0;
+
+    if (ret == 0)
+        ret = g_quark_from_static_string ("nm_logging_error");
+    return ret;
+}
+
+GType
+nm_logging_error_get_type (void)
+{
+    static GType etype = 0;
+
+    if (etype == 0) {
+        static const GEnumValue values[] = {
+            ENUM_ENTRY (NM_LOGGING_ERROR_UNKNOWN_LEVEL,  "UnknownLevel"),
+            ENUM_ENTRY (NM_LOGGING_ERROR_UNKNOWN_DOMAIN, "UnknownDomain"),
+            { 0, 0, 0 }
+        };
+        etype = g_enum_register_static ("NMLoggingError", values);
+    }
+    return etype;
+}
+
+/************************************************************************/
+
+gboolean
+nm_logging_setup (const char *level, const char *domains, GError **error)
+{
+	char **tmp, **iter;
+	guint32 new_domains = 0;
+
+	/* levels */
+	if (level && strlen (level)) {
+		gboolean found = FALSE;
+		const LogDesc *diter;
+
+		for (diter = &level_descs[0]; diter->name; diter++) {
+			if (!strcasecmp (diter->name, level)) {
+				log_level = diter->num;
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found) {
+			g_set_error (error, NM_LOGGING_ERROR, NM_LOGGING_ERROR_UNKNOWN_LEVEL,
+			             _("Unknown log level '%s'"), level);
+			return FALSE;
+		}
+	}
+
+	/* domains */
+	if (domains && strlen (domains)) {
+		tmp = g_strsplit_set (domains, ", ", 0);
+		for (iter = tmp; iter && *iter; iter++) {
+			const LogDesc *diter;
+			gboolean found = FALSE;
+
+			if (!strlen (*iter))
+				continue;
+
+			for (diter = &domain_descs[0]; diter->name; diter++) {
+				if (!strcasecmp (diter->name, *iter)) {
+					new_domains |= diter->num;
+					found = TRUE;
+					break;
+				}
+			}
+
+			if (!found) {
+				g_set_error (error, NM_LOGGING_ERROR, NM_LOGGING_ERROR_UNKNOWN_DOMAIN,
+				             _("Unknown log domain '%s'"), *iter);
+				return FALSE;
+			}
+		}
+		g_strfreev (tmp);
+		log_domains = new_domains;
+	}
+
+	return TRUE;
+}
+
+const char *
+nm_logging_level_to_string (void)
+{
+	const LogDesc *diter;
+
+	for (diter = &level_descs[0]; diter->name; diter++) {
+		if (diter->num == log_level)
+			return diter->name;
+	}
+	g_warn_if_reached ();
+	return "";
+}
+
+char *
+nm_logging_domains_to_string (void)
+{
+	const LogDesc *diter;
+	GString *str;
+
+	str = g_string_sized_new (75);
+	for (diter = &domain_descs[0]; diter->name; diter++) {
+		if (diter->num & log_domains) {
+			if (str->len)
+				g_string_append_c (str, ',');
+			g_string_append (str, diter->name);
+		}
+	}
+	return g_string_free (str, FALSE);
+}
+
+void _nm_log (const char *loc,
+              const char *func,
+              guint32 domain,
+              guint32 level,
+              const char *fmt,
+              ...)
+{
+	va_list args;
+	char *msg;
+	GTimeVal tv;
+
+	if (!(log_level & level) || !(log_domains & domain))
+		return;
+
+	va_start (args, fmt);
+	msg = g_strdup_vprintf (fmt, args);
+	va_end (args);
+
+	if ((log_level & LOGL_DEBUG) && (level == LOGL_DEBUG)) {
+		g_get_current_time (&tv);
+		syslog (LOG_INFO, "<debug> [%ld.%ld] [%s] %s(): %s\n", tv.tv_sec, tv.tv_usec, loc, func, msg);
+	} else if ((log_level & LOGL_INFO) && (level == LOGL_INFO))
+		syslog (LOG_INFO, "<info> %s\n", msg);
+	else if ((log_level & LOGL_WARN) && (level == LOGL_WARN))
+		syslog (LOG_WARNING, "<warn> %s\n", msg);
+	else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) {
+		g_get_current_time (&tv);
+		syslog (LOG_ERR, "<error> [%ld.%ld] [%s] %s(): %s\n", tv.tv_sec, tv.tv_usec, loc, func, msg);
+	}
+	g_free (msg);
+}
+
+/************************************************************************/
+
+static void
+fallback_get_backtrace (void)
+{
+	void *frames[64];
+	Dl_info info;
+	size_t size;
+	guint32 i;
+	const char *name;
+
+	size = backtrace (frames, G_N_ELEMENTS (frames));
+
+	syslog (LOG_CRIT, "******************* START **********************************");
+	for (i = 0; i < size; i++) {
+		dladdr (frames[i], &info);
+		name = (info.dli_fname && *info.dli_fname) ? info.dli_fname : "(vdso)";
+		if (info.dli_saddr) {
+			syslog (LOG_CRIT, "Frame %d: %s (%s+0x%lx) [%p]",
+			        i, name,
+			        info.dli_sname,
+			        (gulong)(frames[i] - info.dli_saddr),
+			        frames[i]);
+		} else {
+			syslog (LOG_CRIT, "Frame %d: %s (%p+0x%lx) [%p]",
+			        i, name,
+			        info.dli_fbase,
+			        (gulong)(frames[i] - info.dli_saddr),
+			        frames[i]);
+		}
+	}
+	syslog (LOG_CRIT, "******************* END **********************************");
+}
+
+
+static gboolean
+crashlogger_get_backtrace (void)
+{
+	gboolean success = FALSE;
+	int pid;	
+
+	pid = fork();
+	if (pid > 0)
+	{
+		/* Wait for the child to finish */
+		int estatus;
+		if (waitpid (pid, &estatus, 0) != -1)
+		{
+			/* Only succeed if the crashlogger succeeded */
+			if (WIFEXITED (estatus) && (WEXITSTATUS (estatus) == 0))
+				success = TRUE;
+		}
+	}
+	else if (pid == 0)
+	{
+		/* Child process */
+		execl (LIBEXECDIR"/nm-crash-logger",
+				LIBEXECDIR"/nm-crash-logger", NULL);
+	}
+
+	return success;
+}
+
+
+void
+nm_logging_backtrace (void)
+{
+	struct stat s;
+	gboolean fallback = TRUE;
+	
+	/* Try to use gdb via nm-crash-logger if it exists, since
+	 * we get much better information out of it.  Otherwise
+	 * fall back to execinfo.
+	 */
+	if (stat (LIBEXECDIR"/nm-crash-logger", &s) == 0)
+		fallback = crashlogger_get_backtrace () ? FALSE : TRUE;
+
+	if (fallback)
+		fallback_get_backtrace ();
+}
+
+
+static void
+nm_log_handler (const gchar *log_domain,
+                GLogLevelFlags level,
+                const gchar *message,
+                gpointer ignored)
+{
+	int syslog_priority;	
+
+	switch (level) {
+	case G_LOG_LEVEL_ERROR:
+		syslog_priority = LOG_CRIT;
+		break;
+	case G_LOG_LEVEL_CRITICAL:
+		syslog_priority = LOG_ERR;
+		break;
+	case G_LOG_LEVEL_WARNING:
+		syslog_priority = LOG_WARNING;
+		break;
+	case G_LOG_LEVEL_MESSAGE:
+		syslog_priority = LOG_NOTICE;
+		break;
+	case G_LOG_LEVEL_DEBUG:
+		syslog_priority = LOG_DEBUG;
+		break;
+	case G_LOG_LEVEL_INFO:
+	default:
+		syslog_priority = LOG_INFO;
+		break;
+	}
+
+	syslog (syslog_priority, "%s", message);
+}
+
+void
+nm_logging_start (gboolean become_daemon)
+{
+	if (become_daemon)
+		openlog (G_LOG_DOMAIN, LOG_PID, LOG_DAEMON);
+	else
+		openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR | LOG_PID, LOG_USER);
+
+	g_log_set_handler (G_LOG_DOMAIN, 
+	                   G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
+	                   nm_log_handler,
+	                   NULL);
+}
+
+void
+nm_logging_shutdown (void)
+{
+	closelog ();
+}
diff --git a/src/logging/nm-logging.h b/src/logging/nm-logging.h
new file mode 100644
index 00000000..2eedf5ff
--- /dev/null
+++ b/src/logging/nm-logging.h
@@ -0,0 +1,111 @@
+/* -*- 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) 2006 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_LOGGING_H
+#define NM_LOGGING_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+/* Log domains */
+enum {
+	LOGD_NONE       = 0x00000000,
+	LOGD_HW         = 0x00000001, /* Hardware detection and info */
+	LOGD_RFKILL     = 0x00000002,
+	LOGD_ETHER      = 0x00000004,
+	LOGD_WIFI       = 0x00000008,
+	LOGD_BT         = 0x00000010,
+	LOGD_MB         = 0x00000020, /* mobile broadband */
+	LOGD_DHCP4      = 0x00000040,
+	LOGD_DHCP6      = 0x00000080,
+	LOGD_PPP        = 0x00000100,
+	LOGD_WIFI_SCAN  = 0x00000200,
+	LOGD_IP4        = 0x00000400,
+	LOGD_IP6        = 0x00000800,
+	LOGD_AUTOIP4    = 0x00001000,
+	LOGD_DNS        = 0x00002000,
+	LOGD_VPN        = 0x00004000,
+	LOGD_SHARING    = 0x00008000, /* Connection sharing/dnsmasq */
+	LOGD_SUPPLICANT = 0x00010000, /* WiFi and 802.1x */
+	LOGD_USER_SET   = 0x00020000, /* User settings */
+	LOGD_SYS_SET    = 0x00040000, /* System settings */
+	LOGD_SUSPEND    = 0x00080000, /* Suspend/Resume */
+	LOGD_CORE       = 0x00100000, /* Core daemon and policy stuff */
+	LOGD_DEVICE     = 0x00200000, /* Device state and activation */
+	LOGD_OLPC_MESH  = 0x00400000,
+};
+
+#define LOGD_DHCP (LOGD_DHCP4 | LOGD_DHCP6)
+
+/* Log levels */
+enum {
+	LOGL_ERR   = 0x00000001,
+	LOGL_WARN  = 0x00000002,
+	LOGL_INFO  = 0x00000004,
+	LOGL_DEBUG = 0x00000008
+};
+
+#define NM_LOGGING_ERROR (nm_logging_error_quark ())
+#define NM_TYPE_LOGGING_ERROR (nm_logging_error_get_type ())
+GQuark nm_logging_error_quark    (void);
+GType  nm_logging_error_get_type (void);
+
+
+#define nm_log_err(domain, fmt, args...) \
+	{ _nm_log (G_STRLOC, G_STRFUNC, domain, LOGL_ERR, fmt, ##args); }
+
+#define nm_log_warn(domain, fmt, args...) \
+	{ _nm_log (G_STRLOC, G_STRFUNC, domain, LOGL_WARN, fmt, ##args); }
+
+#define nm_log_info(domain, fmt, args...) \
+	{ _nm_log (G_STRLOC, G_STRFUNC, domain, LOGL_INFO, fmt, ##args); }
+
+#define nm_log_dbg(domain, fmt, args...) \
+	{ _nm_log (G_STRLOC, G_STRFUNC, domain, LOGL_DEBUG, fmt, ##args); }
+
+#define nm_log(domain, level, fmt, args...) \
+	{ _nm_log (G_STRLOC, G_STRFUNC, domain, level, fmt, ##args); }
+
+void _nm_log (const char *loc, const char *func,
+              guint32 domain, guint32 level,
+              const char *fmt, ...);
+
+const char *nm_logging_level_to_string (void);
+char *nm_logging_domains_to_string (void);
+
+/* Undefine the nm-utils.h logging stuff to ensure errors */
+#undef nm_print_backtrace
+#undef nm_get_timestamp
+#undef nm_info
+#undef nm_info_str
+#undef nm_debug
+#undef nm_debug_str
+#undef nm_warning
+#undef nm_warning_str
+#undef nm_error
+#undef nm_error_str
+
+gboolean nm_logging_setup     (const char *level, const char *domains, GError **error);
+void     nm_logging_start     (gboolean become_daemon);
+void     nm_logging_backtrace (void);
+void     nm_logging_shutdown  (void);
+
+#endif /* NM_LOGGING_H */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 00000000..c2fb58be
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,758 @@
+/* -*- 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) 2004 - 2010 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <glib/gi18n.h>
+#include <gmodule.h>
+#include <string.h>
+
+#include "NetworkManager.h"
+#include "NetworkManagerUtils.h"
+#include "nm-manager.h"
+#include "nm-policy.h"
+#include "nm-system.h"
+#include "nm-named-manager.h"
+#include "nm-dbus-manager.h"
+#include "nm-supplicant-manager.h"
+#include "nm-dhcp-manager.h"
+#include "nm-hostname-provider.h"
+#include "nm-netlink-monitor.h"
+#include "nm-vpn-manager.h"
+#include "nm-logging.h"
+
+#if !defined(NM_DIST_VERSION)
+# define NM_DIST_VERSION VERSION
+#endif
+
+#define NM_DEFAULT_PID_FILE          LOCALSTATEDIR"/run/NetworkManager.pid"
+#define NM_DEFAULT_SYSTEM_CONF_FILE  SYSCONFDIR"/NetworkManager/NetworkManager.conf"
+#define NM_OLD_SYSTEM_CONF_FILE      SYSCONFDIR"/NetworkManager/nm-system-settings.conf"
+#define NM_DEFAULT_SYSTEM_STATE_FILE LOCALSTATEDIR"/lib/NetworkManager/NetworkManager.state"
+
+/*
+ * Globals
+ */
+static NMManager *manager = NULL;
+static GMainLoop *main_loop = NULL;
+
+typedef struct {
+	time_t time;
+	GQuark domain;
+	guint32 code;
+	guint32 count;
+} MonitorInfo;
+
+static gboolean
+detach_monitor (gpointer data)
+{
+	nm_log_warn (LOGD_HW, "detaching netlink event monitor");
+	nm_netlink_monitor_detach (NM_NETLINK_MONITOR (data));
+	return FALSE;
+}
+
+static void
+nm_error_monitoring_device_link_state (NMNetlinkMonitor *monitor,
+									   GError *error,
+									   gpointer user_data)
+{
+	MonitorInfo *info = (MonitorInfo *) user_data;
+	time_t now;
+
+	now = time (NULL);
+
+	if (   (info->domain != error->domain)
+	    || (info->code != error->code)
+	    || (info->time && now > info->time + 10)) {
+		/* FIXME: Try to handle the error instead of just printing it. */
+		nm_log_warn (LOGD_HW, "error monitoring device for netlink events: %s\n", error->message);
+
+		info->time = now;
+		info->domain = error->domain;
+		info->code = error->code;
+		info->count = 0;
+	}
+
+	info->count++;
+	if (info->count > 100) {
+		/* Broken drivers will sometimes cause a flood of netlink errors.
+		 * rh #459205, novell #443429, lp #284507
+		 */
+		nm_log_warn (LOGD_HW, "excessive netlink errors ocurred, disabling netlink monitor.");
+		nm_log_warn (LOGD_HW, "link change events will not be processed.");
+		g_idle_add_full (G_PRIORITY_HIGH, detach_monitor, monitor, NULL);
+	}
+}
+
+static gboolean
+nm_monitor_setup (GError **error)
+{
+	NMNetlinkMonitor *monitor;
+	MonitorInfo *info;
+
+	monitor = nm_netlink_monitor_get ();
+	if (!nm_netlink_monitor_open_connection (monitor, error)) {
+		g_object_unref (monitor);
+		return FALSE;
+	}
+
+	info = g_new0 (MonitorInfo, 1);
+	g_signal_connect_data (G_OBJECT (monitor), "error",
+						   G_CALLBACK (nm_error_monitoring_device_link_state),
+						   info,
+						   (GClosureNotify) g_free,
+						   0);
+	nm_netlink_monitor_attach (monitor);
+
+	/* Request initial status of cards */
+	nm_netlink_monitor_request_status (monitor, NULL);
+
+	return TRUE;
+}
+
+static gboolean quit_early = FALSE;
+
+static void
+nm_signal_handler (int signo)
+{
+	static int in_fatal = 0;
+
+	/* avoid loops */
+	if (in_fatal > 0)
+		return;
+	++in_fatal;
+
+	switch (signo)
+	{
+		case SIGSEGV:
+		case SIGBUS:
+		case SIGILL:
+		case SIGABRT:
+			nm_log_warn (LOGD_CORE, "caught signal %d. Generating backtrace...", signo);
+			nm_logging_backtrace ();
+			exit (1);
+			break;
+
+		case SIGFPE:
+		case SIGPIPE:
+			/* let the fatal signals interrupt us */
+			--in_fatal;
+
+			nm_log_warn (LOGD_CORE, "caught signal %d, shutting down abnormally. Generating backtrace...", signo);
+			nm_logging_backtrace ();
+			g_main_loop_quit (main_loop);
+			break;
+
+		case SIGINT:
+		case SIGTERM:
+			/* let the fatal signals interrupt us */
+			--in_fatal;
+
+			nm_log_info (LOGD_CORE, "caught signal %d, shutting down normally.", signo);
+			quit_early = TRUE;
+			g_main_loop_quit (main_loop);
+			break;
+
+		case SIGHUP:
+			--in_fatal;
+			/* FIXME:
+			 * Reread config stuff like system config files, VPN service files, etc
+			 */
+			break;
+
+		case SIGUSR1:
+			--in_fatal;
+			/* FIXME:
+			 * Play with log levels or something
+			 */
+			break;
+
+		default:
+			signal (signo, nm_signal_handler);
+			break;
+	}
+}
+
+static void
+setup_signals (void)
+{
+	struct sigaction action;
+	sigset_t mask;
+
+	sigemptyset (&mask);
+	action.sa_handler = nm_signal_handler;
+	action.sa_mask = mask;
+	action.sa_flags = 0;
+	sigaction (SIGTERM,  &action, NULL);
+	sigaction (SIGINT,  &action, NULL);
+	sigaction (SIGILL,  &action, NULL);
+	sigaction (SIGBUS,  &action, NULL);
+	sigaction (SIGFPE,  &action, NULL);
+	sigaction (SIGHUP,  &action, NULL);
+	sigaction (SIGSEGV, &action, NULL);
+	sigaction (SIGABRT, &action, NULL);
+	sigaction (SIGUSR1,  &action, NULL);
+}
+
+static gboolean
+write_pidfile (const char *pidfile)
+{
+ 	char pid[16];
+	int fd;
+	gboolean success = FALSE;
+ 
+	if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) {
+		fprintf (stderr, "Opening %s failed: %s\n", pidfile, strerror (errno));
+		return FALSE;
+	}
+
+ 	snprintf (pid, sizeof (pid), "%d", getpid ());
+	if (write (fd, pid, strlen (pid)) < 0)
+		fprintf (stderr, "Writing to %s failed: %s\n", pidfile, strerror (errno));
+	else
+		success = TRUE;
+
+	if (close (fd))
+		fprintf (stderr, "Closing %s failed: %s\n", pidfile, strerror (errno));
+
+	return success;
+}
+
+/* Check whether the pidfile already exists and contains PID of a running NetworkManager
+ *  Returns:  FALSE - specified pidfile doesn't exist or doesn't contain PID of a running NM process
+ *            TRUE  - specified pidfile already exists and contains PID of a running NM process
+ */
+static gboolean
+check_pidfile (const char *pidfile)
+{
+	char *contents = NULL;
+	gsize len = 0;
+	glong pid;
+	char *proc_cmdline = NULL;
+	gboolean nm_running = FALSE;
+	const char *process_name;
+
+	if (!g_file_get_contents (pidfile, &contents, &len, NULL))
+		return FALSE;
+
+	if (len <= 0)
+		goto done;
+
+	errno = 0;
+	pid = strtol (contents, NULL, 10);
+	if (pid <= 0 || pid > 65536 || errno)
+		goto done;
+
+	g_free (contents);
+	proc_cmdline = g_strdup_printf ("/proc/%ld/cmdline", pid);
+	if (!g_file_get_contents (proc_cmdline, &contents, &len, NULL))
+		goto done;
+
+	process_name = strrchr (contents, '/');
+	if (process_name)
+		process_name++;
+	else
+		process_name = contents;
+	if (strcmp (process_name, "NetworkManager") == 0) {
+		/* Check that the process exists */
+		if (kill (pid, 0) == 0) {
+			fprintf (stderr, "NetworkManager is already running (pid %ld)\n", pid);
+			nm_running = TRUE;
+		}
+	}
+
+done:
+	g_free (proc_cmdline);
+	g_free (contents);
+	return nm_running;
+}
+
+static gboolean
+parse_config_file (const char *filename,
+                   char **plugins,
+                   char **dhcp_client,
+                   char **log_level,
+                   char **log_domains,
+                   GError **error)
+{
+	GKeyFile *config;
+
+	config = g_key_file_new ();
+	if (!config) {
+		g_set_error (error, 0, 0,
+		             "Not enough memory to load config file.");
+		return FALSE;
+	}
+
+	g_key_file_set_list_separator (config, ',');
+	if (!g_key_file_load_from_file (config, filename, G_KEY_FILE_NONE, error))
+		return FALSE;
+
+	*plugins = g_key_file_get_value (config, "main", "plugins", error);
+	if (*error)
+		return FALSE;
+
+	*dhcp_client = g_key_file_get_value (config, "main", "dhcp", NULL);
+
+	*log_level = g_key_file_get_value (config, "logging", "level", NULL);
+	*log_domains = g_key_file_get_value (config, "logging", "domains", NULL);
+
+	g_key_file_free (config);
+	return TRUE;
+}
+
+static gboolean
+parse_state_file (const char *filename,
+                  gboolean *net_enabled,
+                  gboolean *wifi_enabled,
+                  gboolean *wwan_enabled,
+                  GError **error)
+{
+	GKeyFile *state_file;
+	GError *tmp_error = NULL;
+	gboolean wifi, net, wwan;
+
+	g_return_val_if_fail (net_enabled != NULL, FALSE);
+	g_return_val_if_fail (wifi_enabled != NULL, FALSE);
+	g_return_val_if_fail (wwan_enabled != NULL, FALSE);
+
+	state_file = g_key_file_new ();
+	if (!state_file) {
+		g_set_error (error, 0, 0,
+		             "Not enough memory to load state file.");
+		return FALSE;
+	}
+
+	g_key_file_set_list_separator (state_file, ',');
+	if (!g_key_file_load_from_file (state_file, filename, G_KEY_FILE_KEEP_COMMENTS, &tmp_error)) {
+		/* This is kinda ugly; create the file and directory if it doesn't
+		 * exist yet.  We can't rely on distros necessarily creating the
+		 * /var/lib/NetworkManager for us since we have to ensure that
+		 * users upgrading NM get this working too.
+		 */
+		if (   tmp_error->domain == G_FILE_ERROR
+		    && tmp_error->code == G_FILE_ERROR_NOENT) {
+			char *data, *dirname;
+			gsize len = 0;
+			gboolean ret = FALSE;
+
+			/* try to create the directory if it doesn't exist */
+			dirname = g_path_get_dirname (filename);
+			errno = 0;
+			if (mkdir (dirname, 0755) != 0) {
+				if (errno != EEXIST) {
+					g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES,
+					             "Error creating state directory %s: %d", dirname, errno);
+					g_free (dirname);
+					return FALSE;
+				}
+			}
+			g_free (dirname);
+
+			/* Write out the initial state to the state file */
+			g_key_file_set_boolean (state_file, "main", "NetworkingEnabled", *net_enabled);
+			g_key_file_set_boolean (state_file, "main", "WirelessEnabled", *wifi_enabled);
+			g_key_file_set_boolean (state_file, "main", "WWANEnabled", *wwan_enabled);
+
+			data = g_key_file_to_data (state_file, &len, NULL);
+			if (data)
+				ret = g_file_set_contents (filename, data, len, error);
+			g_free (data);
+
+			return ret;
+		} else {
+			g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message);
+			g_clear_error (&tmp_error);
+		}
+
+		/* Otherwise, file probably corrupt or inaccessible */
+		return FALSE;
+	}
+
+	/* Reading state bits of NetworkManager; an error leaves the passed-in state
+	 * value unchanged.
+	 */
+	net = g_key_file_get_boolean (state_file, "main", "NetworkingEnabled", &tmp_error);
+	if (tmp_error)
+		g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message);
+	else
+		*net_enabled = net;
+	g_clear_error (&tmp_error);
+
+	wifi = g_key_file_get_boolean (state_file, "main", "WirelessEnabled", &tmp_error);
+	if (tmp_error) {
+		g_clear_error (error);
+		g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message);
+	} else
+		*wifi_enabled = wifi;
+	g_clear_error (&tmp_error);
+
+	wwan = g_key_file_get_boolean (state_file, "main", "WWANEnabled", &tmp_error);
+	if (tmp_error) {
+		g_clear_error (error);
+		g_set_error_literal (error, tmp_error->domain, tmp_error->code, tmp_error->message);
+	} else
+		*wwan_enabled = wwan;
+	g_clear_error (&tmp_error);
+
+	g_key_file_free (state_file);
+
+	return TRUE;
+}
+
+/*
+ * main
+ *
+ */
+int
+main (int argc, char *argv[])
+{
+	GOptionContext *opt_ctx = NULL;
+	gboolean become_daemon = FALSE;
+	gboolean g_fatal_warnings = FALSE;
+	char *pidfile = NULL, *state_file = NULL, *dhcp = NULL;
+	char *config = NULL, *plugins = NULL, *conf_plugins = NULL;
+	char *log_level = NULL, *log_domains = NULL;
+	gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE;
+	gboolean success;
+	NMPolicy *policy = NULL;
+	NMVPNManager *vpn_manager = NULL;
+	NMNamedManager *named_mgr = NULL;
+	NMDBusManager *dbus_mgr = NULL;
+	NMSupplicantManager *sup_mgr = NULL;
+	NMDHCPManager *dhcp_mgr = NULL;
+	GError *error = NULL;
+	gboolean wrote_pidfile = FALSE;
+	char *cfg_log_level = NULL, *cfg_log_domains = NULL;
+
+	GOptionEntry options[] = {
+		{ "no-daemon", 0, 0, G_OPTION_ARG_NONE, &become_daemon, "Don't become a daemon", NULL },
+		{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, "Make all warnings fatal", NULL },
+		{ "pid-file", 0, 0, G_OPTION_ARG_FILENAME, &pidfile, "Specify the location of a PID file", "filename" },
+		{ "state-file", 0, 0, G_OPTION_ARG_FILENAME, &state_file, "State file location", "/path/to/state.file" },
+		{ "config", 0, 0, G_OPTION_ARG_FILENAME, &config, "Config file location", "/path/to/config.file" },
+		{ "plugins", 0, 0, G_OPTION_ARG_STRING, &plugins, "List of plugins separated by ','", "plugin1,plugin2" },
+		{ "log-level", 0, 0, G_OPTION_ARG_STRING, &log_level, "Log level: one of [ERR, WARN, INFO, DEBUG]", "INFO" },
+		{ "log-domains", 0, 0, G_OPTION_ARG_STRING, &log_domains,
+		        "Log domains separated by ',': any combination of [NONE,HW,RKILL,ETHER,WIFI,BT,MB,DHCP4,DHCP6,PPP,WIFI_SCAN,IP4,IP6,AUTOIP4,DNS,VPN,SHARING,SUPPLICANT,USER_SET,SYS_SET,SUSPEND,CORE,DEVICE,OLPC]",
+		        "HW,RFKILL,WIFI" },
+		{NULL}
+	};
+
+	if (getuid () != 0) {
+		fprintf (stderr, "You must be root to run NetworkManager!\n");
+		exit (1);
+	}
+
+	if (!g_module_supported ()) {
+		fprintf (stderr, "GModules are not supported on your platform!\n");
+		exit (1);
+	}
+
+	bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	/* Parse options */
+	opt_ctx = g_option_context_new ("");
+	g_option_context_set_translation_domain (opt_ctx, "UTF-8");
+	g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
+	g_option_context_set_help_enabled (opt_ctx, TRUE);
+	g_option_context_add_main_entries (opt_ctx, options, NULL);
+
+	g_option_context_set_summary (opt_ctx,
+		"NetworkManager monitors all network connections and automatically\nchooses the best connection to use.  It also allows the user to\nspecify wireless access points which wireless cards in the computer\nshould associate with.");
+
+	success = g_option_context_parse (opt_ctx, &argc, &argv, NULL);
+	g_option_context_free (opt_ctx);
+
+	if (!success) {
+		fprintf (stderr, _("Invalid option.  Please use --help to see a list of valid options.\n"));
+		exit (1);
+	}
+
+	/* Make GIO ignore the remote VFS service; otherwise it tries to use the
+	 * session bus to contact the remote service, and NM shouldn't ever be
+	 * talking on the session bus.  See rh #588745
+	 */
+	setenv ("GIO_USE_VFS", "local", 1);
+
+	pidfile = pidfile ? pidfile : g_strdup (NM_DEFAULT_PID_FILE);
+	state_file = state_file ? state_file : g_strdup (NM_DEFAULT_SYSTEM_STATE_FILE);
+
+	/* check pid file */
+	if (check_pidfile (pidfile))
+		exit (1);
+
+	/* Parse the config file */
+	if (config) {
+		if (!parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error)) {
+			fprintf (stderr, "Config file %s invalid: (%d) %s\n",
+			         config,
+			         error ? error->code : -1,
+			         (error && error->message) ? error->message : "unknown");
+			exit (1);
+		}
+	} else {
+		gboolean parsed = FALSE;
+
+		/* Even though we prefer NetworkManager.conf, we need to check the
+		 * old nm-system-settings.conf first to preserve compat with older
+		 * setups.  In package managed systems dropping a NetworkManager.conf
+		 * onto the system would make NM use it instead of nm-system-settings.conf,
+		 * changing behavior during an upgrade.  We don't want that.
+		 */
+
+		/* Try deprecated nm-system-settings.conf first */
+		if (g_file_test (NM_OLD_SYSTEM_CONF_FILE, G_FILE_TEST_EXISTS)) {
+			config = g_strdup (NM_OLD_SYSTEM_CONF_FILE);
+			parsed = parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error);
+			if (!parsed) {
+				fprintf (stderr, "Default config file %s invalid: (%d) %s\n",
+				         config,
+				         error ? error->code : -1,
+				         (error && error->message) ? error->message : "unknown");
+				g_free (config);
+				config = NULL;
+				g_clear_error (&error);
+			}
+		}
+
+		/* Try the preferred NetworkManager.conf last */
+		if (!parsed && g_file_test (NM_DEFAULT_SYSTEM_CONF_FILE, G_FILE_TEST_EXISTS)) {
+			config = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
+			parsed = parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error);
+			if (!parsed) {
+				fprintf (stderr, "Default config file %s invalid: (%d) %s\n",
+				         config,
+				         error ? error->code : -1,
+				         (error && error->message) ? error->message : "unknown");
+				g_free (config);
+				config = NULL;
+				g_clear_error (&error);
+			}
+		}
+	}
+	/* Logging setup */
+	if (!nm_logging_setup (log_level ? log_level : cfg_log_level,
+	                       log_domains ? log_domains : cfg_log_domains,
+	                       &error)) {
+		fprintf (stderr,
+		         _("%s.  Please use --help to see a list of valid options.\n"),
+		         error->message);
+		exit (1);
+	}
+
+	/* Plugins specified with '--plugins' override those of config file */
+	plugins = plugins ? plugins : g_strdup (conf_plugins);
+	g_free (conf_plugins);
+
+	/* Parse the state file */
+	if (!parse_state_file (state_file, &net_enabled, &wifi_enabled, &wwan_enabled, &error)) {
+		fprintf (stderr, "State file %s parsing failed: (%d) %s\n",
+		         state_file,
+		         error ? error->code : -1,
+		         (error && error->message) ? error->message : "unknown");
+		/* Not a hard failure */
+	}
+	g_clear_error (&error);
+
+	/* Tricky: become_daemon is FALSE by default, so unless it's TRUE because
+	 * of a CLI option, it'll become TRUE after this
+	 */
+	become_daemon = !become_daemon;
+	if (become_daemon) {
+		if (daemon (0, 0) < 0) {
+			int saved_errno;
+
+			saved_errno = errno;
+			fprintf (stderr, "Could not daemonize: %s [error %u]\n",
+			         g_strerror (saved_errno),
+			         saved_errno);
+			exit (1);
+		}
+		if (write_pidfile (pidfile))
+			wrote_pidfile = TRUE;
+	}
+
+	if (g_fatal_warnings) {
+		GLogLevelFlags fatal_mask;
+
+		fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+		fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+		g_log_set_always_fatal (fatal_mask);
+	}
+
+	/*
+	 * Set the umask to 0022, which results in 0666 & ~0022 = 0644.
+	 * Otherwise, if root (or an su'ing user) has a wacky umask, we could
+	 * write out an unreadable resolv.conf.
+	 */
+	umask (022);
+
+	g_type_init ();
+	if (!g_thread_supported ())
+		g_thread_init (NULL);
+	dbus_g_thread_init ();
+
+	setup_signals ();
+
+	nm_logging_start (become_daemon);
+
+	nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting...");
+	success = FALSE;
+
+	if (config)
+		nm_log_info (LOGD_CORE, "Read config file %s", config);
+
+	main_loop = g_main_loop_new (NULL, FALSE);
+
+	/* Create watch functions that monitor cards for link status. */
+	if (!nm_monitor_setup (&error)) {
+		nm_log_err (LOGD_CORE, "failed to start monitoring devices: %s.",
+		            error && error->message ? error->message : "(unknown)");
+		goto done;
+	}
+
+	/* Initialize our DBus service & connection */
+	dbus_mgr = nm_dbus_manager_get ();
+
+	vpn_manager = nm_vpn_manager_get ();
+	if (!vpn_manager) {
+		nm_log_err (LOGD_CORE, "failed to start the VPN manager.");
+		goto done;
+	}
+
+	named_mgr = nm_named_manager_get ();
+	if (!named_mgr) {
+		nm_log_err (LOGD_CORE, "failed to start the named manager.");
+		goto done;
+	}
+
+	manager = nm_manager_get (config,
+	                          plugins,
+	                          state_file,
+	                          net_enabled,
+	                          wifi_enabled,
+	                          wwan_enabled,
+	                          &error);
+	if (manager == NULL) {
+		nm_log_err (LOGD_CORE, "failed to initialize the network manager: %s",
+		          error && error->message ? error->message : "(unknown)");
+		goto done;
+	}
+
+	policy = nm_policy_new (manager, vpn_manager);
+	if (policy == NULL) {
+		nm_log_err (LOGD_CORE, "failed to initialize the policy.");
+		goto done;
+	}
+
+	/* Initialize the supplicant manager */
+	sup_mgr = nm_supplicant_manager_get ();
+	if (!sup_mgr) {
+		nm_log_err (LOGD_CORE, "failed to initialize the supplicant manager.");
+		goto done;
+	}
+
+	/* Initialize DHCP manager */
+	dhcp_mgr = nm_dhcp_manager_new (dhcp, &error);
+	if (!dhcp_mgr) {
+		nm_log_err (LOGD_CORE, "failed to start the DHCP manager: %s.", error->message);
+		goto done;
+	}
+
+	nm_dhcp_manager_set_hostname_provider (dhcp_mgr, NM_HOSTNAME_PROVIDER (manager));
+
+	/* Start our DBus service */
+	if (!nm_dbus_manager_start_service (dbus_mgr)) {
+		nm_log_err (LOGD_CORE, "failed to start the dbus service.");
+		goto done;
+	}
+
+	nm_manager_start (manager);
+
+	/* Bring up the loopback interface. */
+	nm_system_enable_loopback ();
+
+	success = TRUE;
+
+	/* Told to quit before getting to the mainloop by the signal handler */
+	if (quit_early == TRUE)
+		goto done;
+
+	g_main_loop_run (main_loop);
+
+done:
+	if (policy)
+		nm_policy_destroy (policy);
+
+	if (manager)
+		g_object_unref (manager);
+
+	if (vpn_manager)
+		g_object_unref (vpn_manager);
+
+	if (named_mgr)
+		g_object_unref (named_mgr);
+
+	if (dhcp_mgr)
+		g_object_unref (dhcp_mgr);
+
+	if (sup_mgr)
+		g_object_unref (sup_mgr);
+
+	if (dbus_mgr)
+		g_object_unref (dbus_mgr);
+
+	nm_logging_shutdown ();
+
+	if (pidfile && wrote_pidfile)
+		unlink (pidfile);
+
+	/* Free options */
+	g_free (pidfile);
+	g_free (state_file);
+	g_free (config);
+	g_free (plugins);
+	g_free (dhcp);
+	g_free (log_level);
+	g_free (log_domains);
+	g_free (cfg_log_level);
+	g_free (cfg_log_domains);
+
+	nm_log_info (LOGD_CORE, "exiting (%s)", success ? "success" : "error");
+	exit (success ? 0 : 1);
+}
diff --git a/src/modem-manager/Makefile.am b/src/modem-manager/Makefile.am
new file mode 100644
index 00000000..a0f65c1b
--- /dev/null
+++ b/src/modem-manager/Makefile.am
@@ -0,0 +1,35 @@
+INCLUDES = \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/marshallers
+
+noinst_LTLIBRARIES = libmodem-manager.la
+
+libmodem_manager_la_SOURCES = \
+	nm-modem.c \
+	nm-modem.h \
+	nm-modem-cdma.c \
+	nm-modem-cdma.h \
+	nm-modem-gsm.c \
+	nm-modem-gsm.h \
+	nm-modem-manager.h \
+	nm-modem-manager.c \
+	nm-modem-types.h
+
+libmodem_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS)
+
+libmodem_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS)
+
+nm-serial-device-glue.h: $(top_srcdir)/introspection/nm-device-serial.xml
+	dbus-binding-tool --prefix=nm_serial_device --mode=glib-server --output=$@ $<
+
+BUILT_SOURCES = \
+	nm-serial-device-glue.h
+
+CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/modem-manager/Makefile.in b/src/modem-manager/Makefile.in
new file mode 100644
index 00000000..26e67a1d
--- /dev/null
+++ b/src/modem-manager/Makefile.in
@@ -0,0 +1,611 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/modem-manager
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libmodem_manager_la_DEPENDENCIES =  \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1)
+am_libmodem_manager_la_OBJECTS = libmodem_manager_la-nm-modem.lo \
+	libmodem_manager_la-nm-modem-cdma.lo \
+	libmodem_manager_la-nm-modem-gsm.lo \
+	libmodem_manager_la-nm-modem-manager.lo
+libmodem_manager_la_OBJECTS = $(am_libmodem_manager_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libmodem_manager_la_SOURCES)
+DIST_SOURCES = $(libmodem_manager_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/marshallers
+
+noinst_LTLIBRARIES = libmodem-manager.la
+libmodem_manager_la_SOURCES = \
+	nm-modem.c \
+	nm-modem.h \
+	nm-modem-cdma.c \
+	nm-modem-cdma.h \
+	nm-modem-gsm.c \
+	nm-modem-gsm.h \
+	nm-modem-manager.h \
+	nm-modem-manager.c \
+	nm-modem-types.h
+
+libmodem_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS)
+
+libmodem_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS)
+
+BUILT_SOURCES = \
+	nm-serial-device-glue.h
+
+CLEANFILES = $(BUILT_SOURCES)
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) 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/modem-manager/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/modem-manager/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libmodem-manager.la: $(libmodem_manager_la_OBJECTS) $(libmodem_manager_la_DEPENDENCIES) 
+	$(LINK)  $(libmodem_manager_la_OBJECTS) $(libmodem_manager_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_manager_la-nm-modem-cdma.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_manager_la-nm-modem-gsm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_manager_la-nm-modem-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_manager_la-nm-modem.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libmodem_manager_la-nm-modem.lo: nm-modem.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_manager_la-nm-modem.lo -MD -MP -MF $(DEPDIR)/libmodem_manager_la-nm-modem.Tpo -c -o libmodem_manager_la-nm-modem.lo `test -f 'nm-modem.c' || echo '$(srcdir)/'`nm-modem.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libmodem_manager_la-nm-modem.Tpo $(DEPDIR)/libmodem_manager_la-nm-modem.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-modem.c' object='libmodem_manager_la-nm-modem.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_manager_la-nm-modem.lo `test -f 'nm-modem.c' || echo '$(srcdir)/'`nm-modem.c
+
+libmodem_manager_la-nm-modem-cdma.lo: nm-modem-cdma.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_manager_la-nm-modem-cdma.lo -MD -MP -MF $(DEPDIR)/libmodem_manager_la-nm-modem-cdma.Tpo -c -o libmodem_manager_la-nm-modem-cdma.lo `test -f 'nm-modem-cdma.c' || echo '$(srcdir)/'`nm-modem-cdma.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libmodem_manager_la-nm-modem-cdma.Tpo $(DEPDIR)/libmodem_manager_la-nm-modem-cdma.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-modem-cdma.c' object='libmodem_manager_la-nm-modem-cdma.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_manager_la-nm-modem-cdma.lo `test -f 'nm-modem-cdma.c' || echo '$(srcdir)/'`nm-modem-cdma.c
+
+libmodem_manager_la-nm-modem-gsm.lo: nm-modem-gsm.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_manager_la-nm-modem-gsm.lo -MD -MP -MF $(DEPDIR)/libmodem_manager_la-nm-modem-gsm.Tpo -c -o libmodem_manager_la-nm-modem-gsm.lo `test -f 'nm-modem-gsm.c' || echo '$(srcdir)/'`nm-modem-gsm.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libmodem_manager_la-nm-modem-gsm.Tpo $(DEPDIR)/libmodem_manager_la-nm-modem-gsm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-modem-gsm.c' object='libmodem_manager_la-nm-modem-gsm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_manager_la-nm-modem-gsm.lo `test -f 'nm-modem-gsm.c' || echo '$(srcdir)/'`nm-modem-gsm.c
+
+libmodem_manager_la-nm-modem-manager.lo: nm-modem-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_manager_la-nm-modem-manager.lo -MD -MP -MF $(DEPDIR)/libmodem_manager_la-nm-modem-manager.Tpo -c -o libmodem_manager_la-nm-modem-manager.lo `test -f 'nm-modem-manager.c' || echo '$(srcdir)/'`nm-modem-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libmodem_manager_la-nm-modem-manager.Tpo $(DEPDIR)/libmodem_manager_la-nm-modem-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-modem-manager.c' object='libmodem_manager_la-nm-modem-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_manager_la-nm-modem-manager.lo `test -f 'nm-modem-manager.c' || echo '$(srcdir)/'`nm-modem-manager.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) 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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+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."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+	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: all check install install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 uninstall uninstall-am
+
+
+nm-serial-device-glue.h: $(top_srcdir)/introspection/nm-device-serial.xml
+	dbus-binding-tool --prefix=nm_serial_device --mode=glib-server --output=$@ $<
+
+# 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/modem-manager/nm-modem-cdma.c b/src/modem-manager/nm-modem-cdma.c
new file mode 100644
index 00000000..c32c1822
--- /dev/null
+++ b/src/modem-manager/nm-modem-cdma.c
@@ -0,0 +1,351 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2009 Novell, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-dbus-glib-types.h"
+#include "nm-modem-cdma.h"
+#include "nm-modem-types.h"
+#include "nm-device.h"
+#include "nm-dbus-manager.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-cdma.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+
+G_DEFINE_TYPE (NMModemCdma, nm_modem_cdma, NM_TYPE_MODEM)
+
+#define NM_MODEM_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_CDMA, NMModemCdmaPrivate))
+
+typedef struct {
+	DBusGProxyCall *call;
+
+	GHashTable *connect_properties;
+} NMModemCdmaPrivate;
+
+
+typedef enum {
+	NM_CDMA_ERROR_CONNECTION_NOT_CDMA = 0,
+	NM_CDMA_ERROR_CONNECTION_INVALID,
+	NM_CDMA_ERROR_CONNECTION_INCOMPATIBLE,
+} NMCdmaError;
+
+#define NM_CDMA_ERROR (nm_cdma_error_quark ())
+#define NM_TYPE_CDMA_ERROR (nm_cdma_error_get_type ())
+
+static GQuark
+nm_cdma_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-cdma-error");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+static GType
+nm_cdma_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Connection was not a CDMA connection. */
+			ENUM_ENTRY (NM_CDMA_ERROR_CONNECTION_NOT_CDMA, "ConnectionNotCdma"),
+			/* Connection was not a valid CDMA connection. */
+			ENUM_ENTRY (NM_CDMA_ERROR_CONNECTION_INVALID, "ConnectionInvalid"),
+			/* Connection does not apply to this device. */
+			ENUM_ENTRY (NM_CDMA_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMCdmaError", values);
+	}
+	return etype;
+}
+
+
+NMModem *
+nm_modem_cdma_new (const char *path,
+                   const char *device,
+                   const char *data_device,
+                   guint32 ip_method)
+{
+	g_return_val_if_fail (path != NULL, NULL);
+	g_return_val_if_fail (device != NULL, NULL);
+	g_return_val_if_fail (data_device != NULL, NULL);
+
+	return (NMModem *) g_object_new (NM_TYPE_MODEM_CDMA,
+	                                 NM_MODEM_PATH, path,
+	                                 NM_MODEM_DEVICE, device,
+	                                 NM_MODEM_IFACE, data_device,
+	                                 NM_MODEM_IP_METHOD, ip_method,
+	                                 NULL);
+}
+
+static void
+stage1_prepare_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NMModemCdma *self = NM_MODEM_CDMA (user_data);
+	NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	priv->call = NULL;
+
+	if (priv->connect_properties) {
+		g_hash_table_destroy (priv->connect_properties);
+		priv->connect_properties = NULL;
+	}
+
+	if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID))
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
+	else {
+		nm_log_warn (LOGD_MB, "CDMA connection failed: (%d) %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+		g_error_free (error);
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_NONE);
+	}
+}
+
+static void
+do_connect (NMModemCdma *self)
+{
+	NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (self);
+	DBusGProxy *proxy;
+
+	proxy = nm_modem_get_proxy (NM_MODEM (self), MM_DBUS_INTERFACE_MODEM_SIMPLE);
+	priv->call = dbus_g_proxy_begin_call_with_timeout (proxy,
+	                                                   "Connect", stage1_prepare_done,
+	                                                   self, NULL, 120000,
+	                                                   DBUS_TYPE_G_MAP_OF_VARIANT, priv->connect_properties,
+	                                                   G_TYPE_INVALID);
+}
+
+static void
+stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMModemCdma *self = NM_MODEM_CDMA (user_data);
+	GError *error = NULL;
+
+	if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID))
+		do_connect (self);
+	else {
+		nm_log_warn (LOGD_MB, "CDMA modem enable failed: (%d) %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+		g_error_free (error);
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+	}
+}
+
+static GHashTable *
+create_connect_properties (NMConnection *connection)
+{
+	NMSettingCdma *setting;
+	GHashTable *properties;
+	const char *str;
+
+	setting = NM_SETTING_CDMA (nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA));
+	properties = value_hash_create ();
+
+	str = nm_setting_cdma_get_number (setting);
+	if (str)
+		value_hash_add_str (properties, "number", str);
+
+	return properties;
+}
+
+static NMActStageReturn
+real_act_stage1_prepare (NMModem *modem,
+                         NMActRequest *req,
+                         GPtrArray **out_hints,
+                         const char **out_setting_name,
+                         NMDeviceStateReason *reason)
+{
+	NMModemCdma *self = NM_MODEM_CDMA (modem);
+	NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (self);
+	NMConnection *connection;
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	*out_setting_name = nm_connection_need_secrets (connection, out_hints);
+	if (!*out_setting_name) {
+		gboolean enabled = nm_modem_get_mm_enabled (modem);
+		DBusGProxy *proxy;
+
+		if (priv->connect_properties)
+			g_hash_table_destroy (priv->connect_properties);
+		priv->connect_properties = create_connect_properties (connection);
+
+		if (enabled)
+			do_connect (self);
+		else {
+			proxy = nm_modem_get_proxy (modem, MM_DBUS_INTERFACE_MODEM);
+			dbus_g_proxy_begin_call_with_timeout (proxy,
+			                                      "Enable", stage1_enable_done,
+			                                      modem, NULL, 20000,
+			                                      G_TYPE_BOOLEAN, TRUE,
+			                                      G_TYPE_INVALID);
+		}
+	} else {
+		/* NMModem will handle requesting secrets... */
+	}
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+static NMConnection *
+real_get_best_auto_connection (NMModem *modem,
+							   GSList *connections,
+							   char **specific_object)
+{
+	GSList *iter;
+
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		NMConnection *connection = NM_CONNECTION (iter->data);
+		NMSettingConnection *s_con;
+
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+		g_assert (s_con);
+
+		if (!nm_setting_connection_get_autoconnect (s_con))
+			continue;
+
+		if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_CDMA_SETTING_NAME))
+			continue;
+
+		return connection;
+	}
+	return NULL;
+}
+
+static gboolean
+real_check_connection_compatible (NMModem *modem,
+                                  NMConnection *connection,
+                                  GError **error)
+{
+	NMSettingConnection *s_con;
+	NMSettingCdma *s_cdma;
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_CDMA_SETTING_NAME)) {
+		g_set_error (error,
+		             NM_CDMA_ERROR, NM_CDMA_ERROR_CONNECTION_NOT_CDMA,
+		             "The connection was not a CDMA connection.");
+		return FALSE;
+	}
+
+	s_cdma = NM_SETTING_CDMA (nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA));
+	if (!s_cdma) {
+		g_set_error (error,
+		             NM_CDMA_ERROR, NM_CDMA_ERROR_CONNECTION_INVALID,
+		             "The connection was not a valid CDMA connection.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+real_get_user_pass (NMModem *modem,
+                    NMConnection *connection,
+                    const char **user,
+                    const char **pass)
+{
+	NMSettingCdma *s_cdma;
+
+	s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA);
+	if (!s_cdma)
+		return FALSE;
+
+	if (user)
+		*user = nm_setting_cdma_get_username (s_cdma);
+	if (pass)
+		*pass = nm_setting_cdma_get_password (s_cdma);
+
+	return TRUE;
+}
+
+static const char *
+real_get_setting_name (NMModem *modem)
+{
+	return NM_SETTING_CDMA_SETTING_NAME;
+}
+
+static void
+real_deactivate_quickly (NMModem *modem, NMDevice *device)
+{
+	NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (modem);
+
+	if (priv->call) {
+		DBusGProxy *proxy;
+
+		proxy = nm_modem_get_proxy (modem, MM_DBUS_INTERFACE_MODEM_SIMPLE);
+		dbus_g_proxy_cancel_call (proxy, priv->call);
+		priv->call = NULL;
+	}
+
+	NM_MODEM_CLASS (nm_modem_cdma_parent_class)->deactivate_quickly (modem, device);	
+}
+
+/*****************************************************************************/
+
+static void
+nm_modem_cdma_init (NMModemCdma *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+	NMModemCdma *self = NM_MODEM_CDMA (object);
+	NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (self);
+
+	if (priv->connect_properties)
+		g_hash_table_destroy (priv->connect_properties);
+
+	G_OBJECT_CLASS (nm_modem_cdma_parent_class)->dispose (object);
+}
+
+static void
+nm_modem_cdma_class_init (NMModemCdmaClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMModemClass *modem_class = NM_MODEM_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMModemCdmaPrivate));
+
+	/* Virtual methods */
+	object_class->dispose = dispose;
+	modem_class->get_user_pass = real_get_user_pass;
+	modem_class->get_setting_name = real_get_setting_name;
+	modem_class->get_best_auto_connection = real_get_best_auto_connection;
+	modem_class->check_connection_compatible = real_check_connection_compatible;
+	modem_class->act_stage1_prepare = real_act_stage1_prepare;
+	modem_class->deactivate_quickly = real_deactivate_quickly;
+
+	dbus_g_error_domain_register (NM_CDMA_ERROR, NULL, NM_TYPE_CDMA_ERROR);
+}
diff --git a/src/modem-manager/nm-modem-cdma.h b/src/modem-manager/nm-modem-cdma.h
new file mode 100644
index 00000000..0abe4e0c
--- /dev/null
+++ b/src/modem-manager/nm-modem-cdma.h
@@ -0,0 +1,56 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 Novell, Inc.
+ */
+
+#ifndef NM_MODEM_CDMA_H
+#define NM_MODEM_CDMA_H
+
+#include <nm-modem.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_MODEM_CDMA			  (nm_modem_cdma_get_type ())
+#define NM_MODEM_CDMA(obj)			  (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_CDMA, NMModemCdma))
+#define NM_MODEM_CDMA_CLASS(klass)	  (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_MODEM_CDMA, NMModemCdmaClass))
+#define NM_IS_MODEM_CDMA(obj)		  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_CDMA))
+#define NM_IS_MODEM_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_MODEM_CDMA))
+#define NM_MODEM_CDMA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_MODEM_CDMA, NMModemCdmaClass))
+
+typedef struct {
+	NMModem parent;
+} NMModemCdma;
+
+typedef struct {
+	NMModemClass parent;
+
+	/* Signals */
+	void (*signal_quality) (NMModemCdma *self, guint32 quality);
+} NMModemCdmaClass;
+
+GType nm_modem_cdma_get_type (void);
+
+NMModem *nm_modem_cdma_new (const char *path,
+                            const char *device,
+                            const char *data_device,
+                            guint32 ip_method);
+
+G_END_DECLS
+
+#endif /* NM_MODEM_CDMA_H */
diff --git a/src/modem-manager/nm-modem-gsm.c b/src/modem-manager/nm-modem-gsm.c
new file mode 100644
index 00000000..abb96db1
--- /dev/null
+++ b/src/modem-manager/nm-modem-gsm.c
@@ -0,0 +1,554 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2009 Novell, Inc.
+ */
+
+#include <string.h>
+#include "nm-dbus-glib-types.h"
+#include "nm-modem-gsm.h"
+#include "nm-device.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-gsm.h"
+#include "nm-modem-types.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+
+typedef enum {
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY = 0,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA,
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA,
+
+    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_LAST = MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA
+} MMModemDeprecatedMode;
+
+typedef enum {
+    MM_MODEM_GSM_ALLOWED_MODE_ANY          = 0,
+    MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED = 1,
+    MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED = 2,
+    MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY      = 3,
+    MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY      = 4,
+
+    MM_MODEM_GSM_ALLOWED_MODE_LAST = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY
+} MMModemGsmAllowedMode;
+
+
+G_DEFINE_TYPE (NMModemGsm, nm_modem_gsm, NM_TYPE_MODEM)
+
+#define NM_MODEM_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_GSM, NMModemGsmPrivate))
+
+typedef struct {
+	DBusGProxyCall *call;
+
+	GHashTable *connect_properties;
+	guint32 pin_tries;
+} NMModemGsmPrivate;
+
+
+typedef enum {
+	NM_GSM_ERROR_CONNECTION_NOT_GSM = 0,
+	NM_GSM_ERROR_CONNECTION_INVALID,
+	NM_GSM_ERROR_CONNECTION_INCOMPATIBLE,
+} NMGsmError;
+
+#define NM_GSM_ERROR (nm_gsm_error_quark ())
+#define NM_TYPE_GSM_ERROR (nm_gsm_error_get_type ())
+
+static GQuark
+nm_gsm_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-gsm-error");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+static GType
+nm_gsm_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Connection was not a GSM connection. */
+			ENUM_ENTRY (NM_GSM_ERROR_CONNECTION_NOT_GSM, "ConnectionNotGsm"),
+			/* Connection was not a valid GSM connection. */
+			ENUM_ENTRY (NM_GSM_ERROR_CONNECTION_INVALID, "ConnectionInvalid"),
+			/* Connection does not apply to this device. */
+			ENUM_ENTRY (NM_GSM_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMGsmError", values);
+	}
+	return etype;
+}
+
+
+NMModem *
+nm_modem_gsm_new (const char *path,
+                  const char *device,
+                  const char *data_device,
+                  guint32 ip_method)
+{
+	g_return_val_if_fail (path != NULL, NULL);
+	g_return_val_if_fail (device != NULL, NULL);
+	g_return_val_if_fail (data_device != NULL, NULL);
+
+	return (NMModem *) g_object_new (NM_TYPE_MODEM_GSM,
+	                                 NM_MODEM_PATH, path,
+	                                 NM_MODEM_DEVICE, device,
+	                                 NM_MODEM_IFACE, data_device,
+	                                 NM_MODEM_IP_METHOD, ip_method,
+	                                 NULL);
+}
+
+static NMDeviceStateReason
+translate_mm_error (GError *error)
+{
+	NMDeviceStateReason reason;
+
+	if (dbus_g_error_has_name (error, MM_MODEM_CONNECT_ERROR_NO_CARRIER))
+		reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER;
+	if (dbus_g_error_has_name (error, MM_MODEM_CONNECT_ERROR_NO_DIALTONE))
+		reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT;
+	if (dbus_g_error_has_name (error, MM_MODEM_CONNECT_ERROR_BUSY))
+		reason = NM_DEVICE_STATE_REASON_MODEM_BUSY;
+	if (dbus_g_error_has_name (error, MM_MODEM_CONNECT_ERROR_NO_ANSWER))
+		reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT;
+	if (dbus_g_error_has_name (error, MM_MODEM_ERROR_NETWORK_NOT_ALLOWED))
+		reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED;
+	if (dbus_g_error_has_name (error, MM_MODEM_ERROR_NETWORK_TIMEOUT))
+		reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT;
+	if (dbus_g_error_has_name (error, MM_MODEM_ERROR_NO_NETWORK))
+		reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING;
+
+	/* FIXME: We have only GSM error messages here, and we have no idea which 
+	   activation state failed. Reasons like:
+	   NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED,
+	   NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED,
+	   NM_DEVICE_STATE_REASON_GSM_APN_FAILED,
+	   NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED,
+	   NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED
+	   are not used.
+	*/
+	else
+		reason = NM_DEVICE_STATE_REASON_UNKNOWN;
+
+	return reason;
+}
+
+static void
+ask_for_pin (NMModemGsm *self, gboolean always_ask)
+{
+	NMModemGsmPrivate *priv;
+	guint32 tries = 0;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_MODEM_GSM (self));
+
+	priv = NM_MODEM_GSM_GET_PRIVATE (self);
+
+	if (!always_ask)
+		tries = priv->pin_tries++;
+
+	g_signal_emit_by_name (self, NM_MODEM_NEED_AUTH,
+	                       NM_SETTING_GSM_SETTING_NAME,
+	                       (tries || always_ask) ? TRUE : FALSE,
+	                       SECRETS_CALLER_MOBILE_BROADBAND,
+	                       NM_SETTING_GSM_PIN,
+	                       NULL);
+}
+
+static void
+stage1_prepare_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NMModemGsm *self = NM_MODEM_GSM (user_data);
+	NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	priv->call = NULL;
+
+	if (priv->connect_properties) {
+		g_hash_table_destroy (priv->connect_properties);
+		priv->connect_properties = NULL;
+	}
+
+	if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID))
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
+	else {
+		if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_PIN))
+			ask_for_pin (self, FALSE);
+		else if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_WRONG))
+			ask_for_pin (self, TRUE);
+		else {
+			nm_log_warn (LOGD_MB, "GSM connection failed: (%d) %s",
+			             error ? error->code : -1,
+			             error && error->message ? error->message : "(unknown)");
+
+			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error));
+		}
+
+		g_error_free (error);
+	}
+}
+
+static void
+do_connect (NMModemGsm *self)
+{
+	NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self);
+	DBusGProxy *proxy;
+
+	proxy = nm_modem_get_proxy (NM_MODEM (self), MM_DBUS_INTERFACE_MODEM_SIMPLE);
+	dbus_g_proxy_begin_call_with_timeout (proxy,
+	                                      "Connect", stage1_prepare_done,
+	                                      self, NULL, 120000,
+	                                      DBUS_TYPE_G_MAP_OF_VARIANT, priv->connect_properties,
+	                                      G_TYPE_INVALID);
+}
+
+static void stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data);
+
+static void
+do_enable (NMModemGsm *self)
+{
+	DBusGProxy *proxy;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_MODEM_GSM (self));
+
+	proxy = nm_modem_get_proxy (NM_MODEM (self), MM_DBUS_INTERFACE_MODEM);
+	dbus_g_proxy_begin_call_with_timeout (proxy,
+	                                      "Enable", stage1_enable_done,
+	                                      self, NULL, 20000,
+	                                      G_TYPE_BOOLEAN, TRUE,
+	                                      G_TYPE_INVALID);
+}
+
+static void
+stage1_pin_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMModemGsm *self = NM_MODEM_GSM (user_data);
+	GError *error = NULL;
+
+	if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) {
+		/* Success; go back and try the enable again */
+		do_enable (self);
+	} else {
+		nm_log_warn (LOGD_MB, "GSM PIN unlock failed: (%d) %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+		g_error_free (error);
+
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+	}
+}
+
+static void
+handle_enable_pin_required (NMModemGsm *self)
+{
+	NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self);
+	const char *pin = NULL;
+	GValue *value;
+	DBusGProxy *proxy;
+
+	/* See if we have a PIN already */
+	value = g_hash_table_lookup (priv->connect_properties, "pin");
+	if (value && G_VALUE_HOLDS_STRING (value))
+		pin = g_value_get_string (value);
+
+	/* If we do, send it */
+	if (pin) {
+		proxy = nm_modem_get_proxy (NM_MODEM (self), MM_DBUS_INTERFACE_MODEM_GSM_CARD);
+		dbus_g_proxy_begin_call_with_timeout (proxy,
+		                                      "SendPin", stage1_pin_done,
+		                                      self, NULL, 10000,
+		                                      G_TYPE_STRING, pin,
+		                                      G_TYPE_INVALID);
+	} else
+		ask_for_pin (self, FALSE);
+}
+
+static void
+stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMModemGsm *self = NM_MODEM_GSM (user_data);
+	GError *error = NULL;
+
+	if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID))
+		do_connect (self);
+	else {
+		nm_log_warn (LOGD_MB, "GSM modem enable failed: (%d) %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+
+		if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_PIN))
+			handle_enable_pin_required (self);
+		else
+			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+
+		g_error_free (error);
+	}
+}
+
+
+static GHashTable *
+create_connect_properties (NMConnection *connection)
+{
+	NMSettingGsm *setting;
+	GHashTable *properties;
+	const char *str;
+
+	setting = NM_SETTING_GSM (nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM));
+	properties = value_hash_create ();
+
+	str = nm_setting_gsm_get_number (setting);
+	if (str)
+		value_hash_add_str (properties, "number", str);
+
+	str = nm_setting_gsm_get_apn (setting);
+	if (str)
+		value_hash_add_str (properties, "apn", str);
+
+	str = nm_setting_gsm_get_network_id (setting);
+	if (str)
+		value_hash_add_str (properties, "network_id", str);
+
+	str = nm_setting_gsm_get_pin (setting);
+	if (str)
+		value_hash_add_str (properties, "pin", str);
+
+	str = nm_setting_gsm_get_username (setting);
+	if (str)
+		value_hash_add_str (properties, "username", str);
+
+	str = nm_setting_gsm_get_password (setting);
+	if (str)
+		value_hash_add_str (properties, "password", str);
+
+	/* Add both old and new preferred modes */
+	switch (nm_setting_gsm_get_network_type (setting)) {
+	case NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA:
+		value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY);
+		value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY);
+		break;
+	case NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE:
+		value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY);
+		value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY);
+		break;
+	case NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA:
+		value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED);
+		value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED);
+		break;
+	case NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE:
+		value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED);
+		value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED);
+		break;
+	default:
+		value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY);
+		value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_ANY);
+		break;
+	}
+
+	/* Roaming */
+	if (nm_setting_gsm_get_home_only (setting))
+		value_hash_add_bool (properties, "home_only", TRUE);
+
+	return properties;
+}
+
+static NMActStageReturn
+real_act_stage1_prepare (NMModem *modem,
+                         NMActRequest *req,
+                         GPtrArray **out_hints,
+                         const char **out_setting_name,
+                         NMDeviceStateReason *reason)
+{
+	NMModemGsm *self = NM_MODEM_GSM (modem);
+	NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self);
+	NMConnection *connection;
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	*out_setting_name = nm_connection_need_secrets (connection, out_hints);
+	if (!*out_setting_name) {
+		gboolean enabled = nm_modem_get_mm_enabled (modem);
+
+		if (priv->connect_properties)
+			g_hash_table_destroy (priv->connect_properties);
+		priv->connect_properties = create_connect_properties (connection);
+
+		if (enabled)
+			do_connect (self);
+		else
+			do_enable (self);
+	} else {
+		/* NMModem will handle requesting secrets... */
+	}
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+static NMConnection *
+real_get_best_auto_connection (NMModem *modem,
+							   GSList *connections,
+							   char **specific_object)
+{
+	GSList *iter;
+
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		NMConnection *connection = NM_CONNECTION (iter->data);
+		NMSettingConnection *s_con;
+
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+		g_assert (s_con);
+
+		if (!nm_setting_connection_get_autoconnect (s_con))
+			continue;
+
+		if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_GSM_SETTING_NAME))
+			continue;
+
+		return connection;
+	}
+	return NULL;
+}
+
+static gboolean
+real_check_connection_compatible (NMModem *modem,
+                                  NMConnection *connection,
+                                  GError **error)
+{
+	NMSettingConnection *s_con;
+	NMSettingGsm *s_gsm;
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_GSM_SETTING_NAME)) {
+		g_set_error (error,
+		             NM_GSM_ERROR, NM_GSM_ERROR_CONNECTION_NOT_GSM,
+		             "The connection was not a GSM connection.");
+		return FALSE;
+	}
+
+	s_gsm = NM_SETTING_GSM (nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM));
+	if (!s_gsm) {
+		g_set_error (error,
+		             NM_GSM_ERROR, NM_GSM_ERROR_CONNECTION_INVALID,
+		             "The connection was not a valid GSM connection.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+real_get_user_pass (NMModem *modem,
+                    NMConnection *connection,
+                    const char **user,
+                    const char **pass)
+{
+	NMSettingGsm *s_gsm;
+
+	s_gsm = (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM);
+	if (!s_gsm)
+		return FALSE;
+
+	if (user)
+		*user = nm_setting_gsm_get_username (s_gsm);
+	if (pass)
+		*pass = nm_setting_gsm_get_password (s_gsm);
+
+	return TRUE;
+}
+
+static const char *
+real_get_setting_name (NMModem *modem)
+{
+	return NM_SETTING_GSM_SETTING_NAME;
+}
+
+static void
+real_deactivate_quickly (NMModem *modem, NMDevice *device)
+{
+	NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (modem);
+
+	if (priv->call) {
+		DBusGProxy *proxy;
+
+		proxy = nm_modem_get_proxy (modem, MM_DBUS_INTERFACE_MODEM_SIMPLE);
+		dbus_g_proxy_cancel_call (proxy, priv->call);
+		priv->call = NULL;
+	}
+
+	priv->pin_tries = 0;
+
+	NM_MODEM_CLASS (nm_modem_gsm_parent_class)->deactivate_quickly (modem, device);	
+}
+
+
+/*****************************************************************************/
+
+static void
+nm_modem_gsm_init (NMModemGsm *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+	NMModemGsm *self = NM_MODEM_GSM (object);
+	NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (self);
+
+	if (priv->connect_properties)
+		g_hash_table_destroy (priv->connect_properties);
+
+	G_OBJECT_CLASS (nm_modem_gsm_parent_class)->dispose (object);
+}
+
+static void
+nm_modem_gsm_class_init (NMModemGsmClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMModemClass *modem_class = NM_MODEM_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMModemGsmPrivate));
+
+	/* Virtual methods */
+	object_class->dispose = dispose;
+	modem_class->get_user_pass = real_get_user_pass;
+	modem_class->get_setting_name = real_get_setting_name;
+	modem_class->get_best_auto_connection = real_get_best_auto_connection;
+	modem_class->check_connection_compatible = real_check_connection_compatible;
+	modem_class->act_stage1_prepare = real_act_stage1_prepare;
+	modem_class->deactivate_quickly = real_deactivate_quickly;
+
+	dbus_g_error_domain_register (NM_GSM_ERROR, NULL, NM_TYPE_GSM_ERROR);
+}
diff --git a/src/modem-manager/nm-modem-gsm.h b/src/modem-manager/nm-modem-gsm.h
new file mode 100644
index 00000000..f481bdb8
--- /dev/null
+++ b/src/modem-manager/nm-modem-gsm.h
@@ -0,0 +1,56 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 Novell, Inc.
+ */
+
+#ifndef NM_MODEM_GSM_H
+#define NM_MODEM_GSM_H
+
+#include <nm-modem.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_MODEM_GSM			 (nm_modem_gsm_get_type ())
+#define NM_MODEM_GSM(obj)			 (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_GSM, NMModemGsm))
+#define NM_MODEM_GSM_CLASS(klass)	 (G_TYPE_CHECK_CLASS_CAST ((klass),	 NM_TYPE_MODEM_GSM, NMModemGsmClass))
+#define NM_IS_MODEM_GSM(obj)		 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_GSM))
+#define NM_IS_MODEM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),	 NM_TYPE_MODEM_GSM))
+#define NM_MODEM_GSM_GET_CLASS(obj)	 (G_TYPE_INSTANCE_GET_CLASS ((obj),	 NM_TYPE_MODEM_GSM, NMModemGsmClass))
+
+typedef struct {
+	NMModem parent;
+} NMModemGsm;
+
+typedef struct {
+	NMModemClass parent;
+
+	/* Signals */
+	void (*signal_quality) (NMModemGsm *self, guint32 quality);
+} NMModemGsmClass;
+
+GType nm_modem_gsm_get_type (void);
+
+NMModem *nm_modem_gsm_new (const char *path,
+                           const char *device,
+                           const char *data_device,
+                           guint32 ip_method);
+
+G_END_DECLS
+
+#endif /* NM_MODEM_GSM_H */
diff --git a/src/modem-manager/nm-modem-manager.c b/src/modem-manager/nm-modem-manager.c
new file mode 100644
index 00000000..28f7b94d
--- /dev/null
+++ b/src/modem-manager/nm-modem-manager.c
@@ -0,0 +1,443 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2009 Novell, Inc.
+ * Copyright (C) 2009 Canonical Ltd.
+ */
+
+#include <string.h>
+#include "nm-modem-manager.h"
+#include "nm-logging.h"
+#include "nm-modem.h"
+#include "nm-modem-gsm.h"
+#include "nm-modem-cdma.h"
+#include "nm-dbus-manager.h"
+#include "nm-modem-types.h"
+#include "nm-marshal.h"
+
+#define MODEM_POKE_INTERVAL 120
+
+G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT)
+
+#define NM_MODEM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_MANAGER, NMModemManagerPrivate))
+
+typedef struct {
+	NMDBusManager *dbus_mgr;
+	DBusGProxy *proxy;
+	GHashTable *modems;
+	gboolean disposed;
+	guint poke_id;
+} NMModemManagerPrivate;
+
+enum {
+	MODEM_ADDED,
+	MODEM_REMOVED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+NMModemManager *
+nm_modem_manager_get (void)
+{
+	static NMModemManager *singleton = NULL;
+
+	if (!singleton)
+		singleton = NM_MODEM_MANAGER (g_object_new (NM_TYPE_MODEM_MANAGER, NULL));
+	else
+		g_object_ref (singleton);
+
+	g_assert (singleton);
+	return singleton;
+}
+
+static gboolean
+get_modem_properties (DBusGConnection *connection,
+					  const char *path,
+					  char **device,
+					  char **data_device,
+					  char **driver,
+					  guint32 *type,
+					  guint32 *ip_method)
+{
+	DBusGProxy *proxy;
+	GValue value = { 0 };
+	GError *err = NULL;
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+									   MM_DBUS_SERVICE,
+									   path,
+									   "org.freedesktop.DBus.Properties");
+
+	if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err,
+	                                    G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM,
+	                                    G_TYPE_STRING, "Type",
+	                                    G_TYPE_INVALID,
+	                                    G_TYPE_VALUE, &value,
+	                                    G_TYPE_INVALID)) {
+		*type = g_value_get_uint (&value);
+		g_value_unset (&value);
+	} else {
+		nm_log_warn (LOGD_MB, "could not get device type: %s", err->message);
+		goto out;
+	}
+
+	if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err,
+	                                    G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM,
+	                                    G_TYPE_STRING, "MasterDevice",
+	                                    G_TYPE_INVALID,
+	                                    G_TYPE_VALUE, &value,
+	                                    G_TYPE_INVALID)) {
+		*device = g_value_dup_string (&value);
+		g_value_unset (&value);
+	} else {
+		nm_log_warn (LOGD_MB, "could not get device: %s", err->message);
+		goto out;
+	}
+
+	if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err,
+										G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM,
+										G_TYPE_STRING, "IpMethod",
+										G_TYPE_INVALID,
+										G_TYPE_VALUE, &value,
+										G_TYPE_INVALID)) {
+		*ip_method = g_value_get_uint (&value);
+		g_value_unset (&value);
+	} else {
+		nm_log_warn (LOGD_MB, "could not get IP method: %s", err->message);
+		goto out;
+	}
+
+	if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err,
+										G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM,
+										G_TYPE_STRING, "Device",
+										G_TYPE_INVALID,
+										G_TYPE_VALUE, &value,
+										G_TYPE_INVALID)) {
+		*data_device = g_value_dup_string (&value);
+		g_value_unset (&value);
+	} else {
+		nm_log_warn (LOGD_MB, "could not get modem data device: %s", err->message);
+		goto out;
+	}
+
+	if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err,
+										G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM,
+										G_TYPE_STRING, "Driver",
+										G_TYPE_INVALID,
+										G_TYPE_VALUE, &value,
+										G_TYPE_INVALID)) {
+		*driver = g_value_dup_string (&value);
+		g_value_unset (&value);
+	} else {
+		nm_log_warn (LOGD_MB, "could not get modem driver: %s", err->message);
+		goto out;
+	}
+
+ out:
+	if (err)
+		g_error_free (err);
+
+	g_object_unref (proxy);
+
+	return *data_device && *driver;
+}
+
+static void
+create_modem (NMModemManager *manager, const char *path)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (manager);
+	NMModem *modem = NULL;
+	char *data_device = NULL, *driver = NULL, *master_device = NULL;
+	uint modem_type = MM_MODEM_TYPE_UNKNOWN;
+	uint ip_method = MM_MODEM_IP_METHOD_PPP;
+
+	if (g_hash_table_lookup (priv->modems, path)) {
+		nm_log_warn (LOGD_MB, "modem with path %s already exists, ignoring", path);
+		return;
+	}
+
+	if (!get_modem_properties (nm_dbus_manager_get_connection (priv->dbus_mgr),
+	                           path, &master_device, &data_device, &driver,
+	                           &modem_type, &ip_method))
+		return;
+
+	if (modem_type == MM_MODEM_TYPE_UNKNOWN) {
+		nm_log_warn (LOGD_MB, "modem with path %s has unknown type, ignoring", path);
+		return;
+	}
+
+	if (!master_device || !strlen (master_device)) {
+		nm_log_warn (LOGD_MB, "modem with path %s has unknown device, ignoring", path);
+		return;
+	}
+
+	if (!driver || !strlen (driver)) {
+		nm_log_warn (LOGD_MB, "modem with path %s has unknown driver, ignoring", path);
+		return;
+	}
+
+	if (!data_device || !strlen (data_device)) {
+		nm_log_warn (LOGD_MB, "modem with path %s has unknown data device, ignoring", path);
+		return;
+	}
+
+	if (modem_type == MM_MODEM_TYPE_GSM)
+		modem = nm_modem_gsm_new (path, master_device, data_device, ip_method);
+	else if (modem_type == MM_MODEM_TYPE_CDMA)
+		modem = nm_modem_cdma_new (path, master_device, data_device, ip_method);
+	else
+		nm_log_warn (LOGD_MB, "unknown modem type '%d'", modem_type);
+
+	g_free (data_device);
+
+	if (modem) {
+		g_hash_table_insert (priv->modems, g_strdup (path), modem);
+		g_signal_emit (manager, signals[MODEM_ADDED], 0, modem, driver);
+	}
+
+	g_free (driver);
+}
+
+static void
+modem_added (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+	create_modem (NM_MODEM_MANAGER (user_data), path);
+}
+
+static void
+modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (user_data);
+	NMModem *modem;
+
+	modem = (NMModem *) g_hash_table_lookup (priv->modems, path);
+	if (modem) {
+		g_signal_emit (user_data, signals[MODEM_REMOVED], 0, modem);
+		g_hash_table_remove (priv->modems, path);
+	}
+}
+
+static gboolean
+poke_modem_cb (gpointer user_data)
+{
+	NMModemManager *self = NM_MODEM_MANAGER (user_data);
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+	DBusGConnection *g_connection;
+	DBusGProxy *proxy;
+
+	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	proxy = dbus_g_proxy_new_for_name (g_connection,
+									   MM_DBUS_SERVICE,
+									   MM_DBUS_PATH,
+									   MM_DBUS_INTERFACE);
+
+	dbus_g_proxy_call_no_reply (proxy, "EnumerateDevices", G_TYPE_INVALID);
+	g_object_unref (proxy);
+
+	return TRUE;
+}
+
+static void
+enumerate_devices_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer data)
+{
+	NMModemManager *manager = NM_MODEM_MANAGER (data);
+	GPtrArray *modems;
+	GError *error = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &error,
+								dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &modems,
+								G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_MB, "could not get modem list: %s", error->message);
+		g_error_free (error);
+	} else {
+		int i;
+
+		for (i = 0; i < modems->len; i++) {
+			char *path = (char *) g_ptr_array_index (modems, i);
+
+			create_modem (manager, path);
+			g_free (path);
+		}
+
+		g_ptr_array_free (modems, TRUE);
+	}
+}
+
+static void
+modem_manager_appeared (NMModemManager *self, gboolean enumerate_devices)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	if (priv->poke_id) {
+		g_source_remove (priv->poke_id);
+		priv->poke_id = 0;
+	}
+
+	nm_log_info (LOGD_MB, "modem-manager is now available");
+
+	priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+											 MM_DBUS_SERVICE, MM_DBUS_PATH, MM_DBUS_INTERFACE);
+
+	dbus_g_proxy_add_signal (priv->proxy, "DeviceAdded", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "DeviceAdded",
+								 G_CALLBACK (modem_added), self,
+								 NULL);
+
+	dbus_g_proxy_add_signal (priv->proxy, "DeviceRemoved", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "DeviceRemoved",
+								 G_CALLBACK (modem_removed), self,
+								 NULL);
+
+	if (enumerate_devices)
+		dbus_g_proxy_begin_call (priv->proxy, "EnumerateDevices", enumerate_devices_done, self, NULL, G_TYPE_INVALID);
+}
+
+static gboolean
+remove_one_modem (gpointer key, gpointer value, gpointer user_data)
+{
+	g_signal_emit (user_data, signals[MODEM_REMOVED], 0, value);
+
+	return TRUE;
+}
+
+static void
+modem_manager_disappeared (NMModemManager *self)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	g_hash_table_foreach_remove (priv->modems, remove_one_modem, self);
+
+	if (priv->proxy) {
+		g_object_unref (priv->proxy);
+		priv->proxy = NULL;
+	}
+
+	/* Try to activate the modem-manager */
+	nm_log_info (LOGD_MB, "trying to start the modem manager...");
+	poke_modem_cb (self);
+	priv->poke_id = g_timeout_add_seconds (MODEM_POKE_INTERVAL, poke_modem_cb, self);
+}
+
+static void
+nm_modem_manager_name_owner_changed (NMDBusManager *dbus_mgr,
+									 const char *name,
+									 const char *old_owner,
+									 const char *new_owner,
+									 gpointer user_data)
+{
+	gboolean old_owner_good;
+	gboolean new_owner_good;
+
+	/* Can't handle the signal if its not from the modem service */
+	if (strcmp (MM_DBUS_SERVICE, name) != 0)
+		return;
+
+	old_owner_good = (old_owner && strlen (old_owner));
+	new_owner_good = (new_owner && strlen (new_owner));
+
+	if (!old_owner_good && new_owner_good) {
+		modem_manager_appeared (NM_MODEM_MANAGER (user_data), FALSE);
+	} else if (old_owner_good && !new_owner_good) {
+		nm_log_info (LOGD_MB, "the modem manager disappeared");
+		modem_manager_disappeared (NM_MODEM_MANAGER (user_data));
+	}
+}
+
+/*******************************************************/
+
+static void
+nm_modem_manager_init (NMModemManager *self)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+	priv->dbus_mgr = nm_dbus_manager_get ();
+
+	g_signal_connect (priv->dbus_mgr, "name-owner-changed",
+					  G_CALLBACK (nm_modem_manager_name_owner_changed),
+					  self);
+
+	if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_DBUS_SERVICE))
+		modem_manager_appeared (self, TRUE);
+	else
+		modem_manager_disappeared (self);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (object);
+
+	if (priv->disposed)
+		return;
+
+	priv->disposed = TRUE;
+
+	if (priv->poke_id) {
+		g_source_remove (priv->poke_id);
+		priv->poke_id = 0;
+	}
+
+	g_hash_table_foreach_remove (priv->modems, remove_one_modem, object);
+	g_hash_table_destroy (priv->modems);
+
+	if (priv->proxy) {
+		g_object_unref (priv->proxy);
+		priv->proxy = NULL;
+	}
+
+	if (priv->dbus_mgr) {
+		g_object_unref (priv->dbus_mgr);
+		priv->dbus_mgr = NULL;
+	}
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (nm_modem_manager_parent_class)->dispose (object);
+}
+
+static void
+nm_modem_manager_class_init (NMModemManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMModemManagerPrivate));
+
+	object_class->dispose = dispose;
+
+	/* signals */
+	signals[MODEM_ADDED] =
+		g_signal_new ("modem-added",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMModemManagerClass, modem_added),
+					  NULL, NULL,
+					  _nm_marshal_VOID__OBJECT_STRING,
+					  G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_STRING);
+
+	signals[MODEM_REMOVED] =
+		g_signal_new ("modem-removed",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMModemManagerClass, modem_removed),
+					  NULL, NULL,
+					  g_cclosure_marshal_VOID__OBJECT,
+					  G_TYPE_NONE, 1, G_TYPE_OBJECT);
+}
diff --git a/src/modem-manager/nm-modem-manager.h b/src/modem-manager/nm-modem-manager.h
new file mode 100644
index 00000000..56427baf
--- /dev/null
+++ b/src/modem-manager/nm-modem-manager.h
@@ -0,0 +1,53 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 Novell, Inc.
+ * Copyright (C) 2009 Canonical Ltd.
+ */
+
+#ifndef NM_MODEM_MANAGER_H
+#define NM_MODEM_MANAGER_H
+
+#include <glib-object.h>
+#include "nm-modem.h"
+
+#define NM_TYPE_MODEM_MANAGER				(nm_modem_manager_get_type ())
+#define NM_MODEM_MANAGER(obj)				(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_MANAGER, NMModemManager))
+#define NM_MODEM_MANAGER_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass),	NM_TYPE_MODEM_MANAGER, NMModemManagerClass))
+#define NM_IS_MODEM_MANAGER(obj)			(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_MANAGER))
+#define NM_IS_MODEM_MANAGER_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),	NM_TYPE_MODEM_MANAGER))
+#define NM_MODEM_MANAGER_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),	NM_TYPE_MODEM_MANAGER, NMModemManagerClass))
+
+typedef struct {
+	GObject parent;
+} NMModemManager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*modem_added) (NMModemManager *manager, NMModem *modem, const char *driver);
+
+	void (*modem_removed) (NMModemManager *manager, NMModem *modem);
+} NMModemManagerClass;
+
+GType nm_modem_manager_get_type (void);
+
+NMModemManager *nm_modem_manager_get (void);
+
+#endif /* NM_MODEM_MANAGER_H */
diff --git a/src/modem-manager/nm-modem-types.h b/src/modem-manager/nm-modem-types.h
new file mode 100644
index 00000000..e805cb21
--- /dev/null
+++ b/src/modem-manager/nm-modem-types.h
@@ -0,0 +1,111 @@
+/* -*- 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) 2009 Novell, Inc.
+ */
+
+#ifndef NM_MODEM_TYPES_H
+#define NM_MODEM_TYPES_H
+
+#define MM_DBUS_SERVICE              "org.freedesktop.ModemManager"
+#define MM_DBUS_PATH                 "/org/freedesktop/ModemManager"
+#define MM_DBUS_INTERFACE            "org.freedesktop.ModemManager"
+#define MM_DBUS_INTERFACE_MODEM      "org.freedesktop.ModemManager.Modem"
+#define MM_DBUS_INTERFACE_MODEM_SIMPLE "org.freedesktop.ModemManager.Modem.Simple"
+#define MM_DBUS_INTERFACE_MODEM_CDMA "org.freedesktop.ModemManager.Modem.Cdma"
+
+#define MM_DBUS_INTERFACE_MODEM_GSM_CARD    "org.freedesktop.ModemManager.Modem.Gsm.Card"
+#define MM_DBUS_INTERFACE_MODEM_GSM_NETWORK "org.freedesktop.ModemManager.Modem.Gsm.Network"
+
+#define MM_MODEM_TYPE_UNKNOWN  0
+#define MM_MODEM_TYPE_GSM      1
+#define MM_MODEM_TYPE_CDMA     2
+
+#define MM_MODEM_IP_METHOD_PPP    0
+#define MM_MODEM_IP_METHOD_STATIC 1
+#define MM_MODEM_IP_METHOD_DHCP   2
+
+/* Errors */
+
+#define MM_SERIAL_OPEN_FAILED MM_DBUS_INTERFACE_MODEM ".SerialOpenFailed"
+#define MM_SERIAL_SEND_FAILED MM_DBUS_INTERFACE_MODEM ".SerialSendFailed"
+#define MM_SERIAL_RESPONSE_TIMEOUT MM_DBUS_INTERFACE_MODEM ".SerialResponseTimeout"
+
+#define MM_MODEM_ERROR_GENERAL MM_DBUS_INTERFACE_MODEM ".General"
+#define MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED MM_DBUS_INTERFACE_MODEM ".OperationNotSupported"
+
+#define MM_MODEM_CONNECT_ERROR_NO_CARRIER MM_DBUS_INTERFACE_MODEM ".NoCarrier"
+#define MM_MODEM_CONNECT_ERROR_NO_DIALTONE MM_DBUS_INTERFACE_MODEM ".NoDialtone"
+#define MM_MODEM_CONNECT_ERROR_BUSY MM_DBUS_INTERFACE_MODEM ".Busy"
+#define MM_MODEM_CONNECT_ERROR_NO_ANSWER MM_DBUS_INTERFACE_MODEM ".NoAnswer"
+
+#define MM_MODEM_ERROR "org.freedesktop.ModemManager.Modem.Gsm"
+
+#define MM_MODEM_ERROR_PHONE_FAILURE MM_MODEM_ERROR ".PhoneFailure"
+#define MM_MODEM_ERROR_NO_CONNECTION MM_MODEM_ERROR ".NoConnection"
+#define MM_MODEM_ERROR_LINK_RESERVED MM_MODEM_ERROR ".LinkReserved"
+#define MM_MODEM_ERROR_NOT_ALLOWED MM_MODEM_ERROR ".OperationNotAllowed"
+#define MM_MODEM_ERROR_NOT_SUPPORTED MM_MODEM_ERROR ".OperationNotSupported"
+#define MM_MODEM_ERROR_PH_SIM_PIN MM_MODEM_ERROR ".PhSimPinRequired"
+#define MM_MODEM_ERROR_PH_FSIM_PIN MM_MODEM_ERROR ".PhFSimPinRequired"
+#define MM_MODEM_ERROR_PH_FSIM_PUK MM_MODEM_ERROR ".PhFPukRequired"
+#define MM_MODEM_ERROR_SIM_NOT_INSERTED MM_MODEM_ERROR ".SimNotInserted"
+#define MM_MODEM_ERROR_SIM_PIN MM_MODEM_ERROR ".SimPinRequired"
+#define MM_MODEM_ERROR_SIM_PUK MM_MODEM_ERROR ".SimPukRequired"
+#define MM_MODEM_ERROR_SIM_FAILURE MM_MODEM_ERROR ".SimFailure"
+#define MM_MODEM_ERROR_SIM_BUSY MM_MODEM_ERROR ".SimBusy"
+#define MM_MODEM_ERROR_SIM_WRONG MM_MODEM_ERROR ".SimWrong"
+#define MM_MODEM_ERROR_WRONG_PASSWORD MM_MODEM_ERROR ".IncorrectPassword"
+#define MM_MODEM_ERROR_SIM_PIN2 MM_MODEM_ERROR ".SimPin2Required"
+#define MM_MODEM_ERROR_SIM_PUK2 MM_MODEM_ERROR ".SimPuk2Required"
+#define MM_MODEM_ERROR_MEMORY_FULL MM_MODEM_ERROR ".MemoryFull"
+#define MM_MODEM_ERROR_INVALID_INDEX MM_MODEM_ERROR ".InvalidIndex"
+#define MM_MODEM_ERROR_NOT_FOUND MM_MODEM_ERROR ".NotFound"
+#define MM_MODEM_ERROR_MEMORY_FAILURE MM_MODEM_ERROR ".MemoryFailure"
+#define MM_MODEM_ERROR_TEXT_TOO_LONG MM_MODEM_ERROR ".TextTooLong"
+#define MM_MODEM_ERROR_INVALID_CHARS MM_MODEM_ERROR ".InvalidChars"
+#define MM_MODEM_ERROR_DIAL_STRING_TOO_LONG MM_MODEM_ERROR ".DialStringTooLong"
+#define MM_MODEM_ERROR_DIAL_STRING_INVALID MM_MODEM_ERROR ".InvalidDialString"
+#define MM_MODEM_ERROR_NO_NETWORK MM_MODEM_ERROR ".NoNetwork"
+#define MM_MODEM_ERROR_NETWORK_TIMEOUT MM_MODEM_ERROR ".NetworkTimeout"
+#define MM_MODEM_ERROR_NETWORK_NOT_ALLOWED MM_MODEM_ERROR ".NetworkNotAllowed"
+#define MM_MODEM_ERROR_NETWORK_PIN MM_MODEM_ERROR ".NetworkPinRequired"
+#define MM_MODEM_ERROR_NETWORK_PUK MM_MODEM_ERROR ".NetworkPukRequired"
+#define MM_MODEM_ERROR_NETWORK_SUBSET_PIN MM_MODEM_ERROR ".NetworkSubsetPinRequired"
+#define MM_MODEM_ERROR_NETWORK_SUBSET_PUK MM_MODEM_ERROR ".NetworkSubsetPukRequired"
+#define MM_MODEM_ERROR_SERVICE_PIN MM_MODEM_ERROR ".ServicePinRequired"
+#define MM_MODEM_ERROR_SERVICE_PUK MM_MODEM_ERROR ".ServicePukRequired"
+#define MM_MODEM_ERROR_CORP_PIN MM_MODEM_ERROR ".CorporatePinRequired"
+#define MM_MODEM_ERROR_CORP_PUK MM_MODEM_ERROR ".CorporatePukRequired"
+#define MM_MODEM_ERROR_HIDDEN_KEY MM_MODEM_ERROR ".HiddenKeyRequired"
+#define MM_MODEM_ERROR_EAP_NOT_SUPPORTED MM_MODEM_ERROR ".EapMethodNotSupported"
+#define MM_MODEM_ERROR_INCORRECT_PARAMS MM_MODEM_ERROR ".IncorrectParams"
+#define MM_MODEM_ERROR_UNKNOWN MM_MODEM_ERROR ".Unknown"
+#define MM_MODEM_ERROR_GPRS_ILLEGAL_MS MM_MODEM_ERROR ".GprsIllegalMs"
+#define MM_MODEM_ERROR_GPRS_ILLEGAL_ME MM_MODEM_ERROR ".GprsIllegalMe"
+#define MM_MODEM_ERROR_GPRS_SERVICE_NOT_ALLOWED  MM_MODEM_ERROR ".GprsServiceNotAllowed"
+#define MM_MODEM_ERROR_GPRS_PLMN_NOT_ALLOWED MM_MODEM_ERROR ".GprsPlmnNotAllowed"
+#define MM_MODEM_ERROR_GPRS_LOCATION_NOT_ALLOWED MM_MODEM_ERROR ".GprsLocationNotAllowed"
+#define MM_MODEM_ERROR_GPRS_ROAMING_NOT_ALLOWED MM_MODEM_ERROR ".GprsRoamingNotAllowed"
+#define MM_MODEM_ERROR_GPRS_OPTION_NOT_SUPPORTED MM_MODEM_ERROR ".GprsOptionNotSupported"
+#define MM_MODEM_ERROR_GPRS_NOT_SUBSCRIBED MM_MODEM_ERROR ".GprsNotSubscribed"
+#define MM_MODEM_ERROR_GPRS_OUT_OF_ORDER MM_MODEM_ERROR ".GprsOutOfOrder"
+#define MM_MODEM_ERROR_GPRS_PDP_AUTH_FAILURE MM_MODEM_ERROR ".GprsPdpAuthFailure"
+#define MM_MODEM_ERROR_GPRS_UNKNOWN MM_MODEM_ERROR ".GprsUnspecified"
+#define MM_MODEM_ERROR_GPRS_INVALID_CLASS MM_MODEM_ERROR ".GprsInvalidClass"
+
+#endif /* NM_MODEM_TYPES_H */
diff --git a/src/modem-manager/nm-modem.c b/src/modem-manager/nm-modem.c
new file mode 100644
index 00000000..e07b818b
--- /dev/null
+++ b/src/modem-manager/nm-modem.c
@@ -0,0 +1,1147 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2009 Novell, Inc.
+ */
+
+#include <string.h>
+#include "nm-modem.h"
+#include "nm-system.h"
+#include "nm-dbus-manager.h"
+#include "nm-setting-connection.h"
+#include "nm-marshal.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-modem-types.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+#include "nm-device-private.h"
+#include "nm-device-interface.h"
+#include "nm-dbus-glib-types.h"
+
+#include "nm-serial-device-glue.h"
+
+G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT)
+
+#define NM_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM, NMModemPrivate))
+
+enum {
+	PROP_0,
+	PROP_DEVICE,
+	PROP_IFACE,
+	PROP_PATH,
+	PROP_IP_METHOD,
+	PROP_ENABLED,
+
+	LAST_PROP
+};
+
+typedef struct {
+	NMDBusManager *dbus_mgr;
+	DBusGProxy *proxy;
+	DBusGProxy *props_proxy;
+
+	char *path;
+	NMPPPManager *ppp_manager;
+	NMIP4Config	 *pending_ip4_config;
+	guint32 ip_method;
+	char *device;
+	char *iface;
+
+	guint32 secrets_tries;
+
+	DBusGProxyCall *call;
+
+	gboolean mm_enabled;
+
+	/* PPP stats */
+	guint32 in_bytes;
+	guint32 out_bytes;
+} NMModemPrivate;
+
+enum {
+	PPP_STATS,
+	PPP_FAILED,
+	PREPARE_RESULT,
+	IP4_CONFIG_RESULT,
+	NEED_AUTH,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+NMPPPManager *
+nm_modem_get_ppp_manager (NMModem *self)
+{
+	g_return_val_if_fail (NM_IS_MODEM (self), NULL);
+
+	return NM_MODEM_GET_PRIVATE (self)->ppp_manager;
+}
+
+gboolean
+nm_modem_get_mm_enabled (NMModem *self)
+{
+	g_return_val_if_fail (NM_IS_MODEM (self), TRUE);
+
+	return NM_MODEM_GET_PRIVATE (self)->mm_enabled;
+}
+
+DBusGProxy *
+nm_modem_get_proxy (NMModem *self,
+					const char *interface)
+{
+
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	const char *current_iface;
+
+	g_return_val_if_fail (NM_IS_MODEM (self), NULL);
+
+	/* Default to the default interface. */
+	if (interface == NULL)
+		interface = MM_DBUS_INTERFACE_MODEM;
+
+	if (interface && !strcmp (interface, DBUS_INTERFACE_PROPERTIES))
+		return priv->props_proxy;
+
+	current_iface = dbus_g_proxy_get_interface (priv->proxy);
+	if (!current_iface || strcmp (current_iface, interface))
+		dbus_g_proxy_set_interface (priv->proxy, interface);
+
+	return priv->proxy;
+}
+
+static void
+merge_ip4_config (NMActRequest *req, NMIP4Config *config)
+{
+	NMConnection *connection;
+	NMSettingIP4Config *s_ip4;
+
+	/* Merge user-defined overrides into the IP4Config to be applied */
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	if (s_ip4)
+		nm_utils_merge_ip4_config (config, s_ip4);
+}
+
+/*****************************************************************************/
+/* IP method PPP */
+
+static void
+ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data)
+{
+	switch (status) {
+	case NM_PPP_STATUS_DISCONNECT:
+		g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_DISCONNECT);
+		break;
+	case NM_PPP_STATUS_DEAD:
+		g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_FAILED);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+ppp_ip4_config (NMPPPManager *ppp_manager,
+				const char *iface,
+				NMIP4Config *config,
+				gpointer user_data)
+{
+	NMModem *self = NM_MODEM (user_data);
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	guint32 i, num;
+	guint32 bad_dns1 = htonl (0x0A0B0C0D);
+	guint32 good_dns1 = htonl (0x04020201);  /* GTE nameserver */
+	guint32 bad_dns2 = htonl (0x0A0B0C0E);
+	guint32 good_dns2 = htonl (0x04020202);  /* GTE nameserver */
+	gboolean dns_workaround = FALSE;
+
+	/* Work around a PPP bug (#1732) which causes many mobile broadband
+	 * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers.
+	 * Apparently fixed in ppp-2.4.5 but we've had some reports that this is
+	 * not the case.
+	 *
+	 * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=2e09ef6886bbf00bc5a9a641110f801e372ffde6
+	 * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=f8191bf07df374f119a07910a79217c7618f113e
+	 */
+
+	num = nm_ip4_config_get_num_nameservers (config);
+	if (num == 2) {
+		gboolean found1 = FALSE, found2 = FALSE;
+
+		for (i = 0; i < num; i++) {
+			guint32 ns = nm_ip4_config_get_nameserver (config, i);
+
+			if (ns == bad_dns1)
+				found1 = TRUE;
+			else if (ns == bad_dns2)
+				found2 = TRUE;
+		}
+
+		/* Be somewhat conservative about substitutions; the "bad" nameservers
+		 * could actually be valid in some cases, so only substitute if ppp
+		 * returns *only* the two bad nameservers.
+		 */
+		dns_workaround = (found1 && found2);
+	}
+
+	if (!num || dns_workaround) {
+		nm_log_warn (LOGD_PPP, "compensating for invalid PPP-provided nameservers");
+		nm_ip4_config_reset_nameservers (config);
+		nm_ip4_config_add_nameserver (config, good_dns1);
+		nm_ip4_config_add_nameserver (config, good_dns2);
+	}
+
+	priv->pending_ip4_config = g_object_ref (config);
+	g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, iface, config, NULL);
+}
+
+static void
+ppp_stats (NMPPPManager *ppp_manager,
+		   guint32 in_bytes,
+		   guint32 out_bytes,
+		   gpointer user_data)
+{
+	NMModem *self = NM_MODEM (user_data);
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+	if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) {
+		priv->in_bytes = in_bytes;
+		priv->out_bytes = out_bytes;
+
+		g_signal_emit (self, signals[PPP_STATS], 0, in_bytes, out_bytes);
+	}
+}
+
+static NMActStageReturn
+ppp_stage3_ip4_config_start (NMModem *self,
+                             NMActRequest *req,
+                             NMDeviceStateReason *reason)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	const char *ppp_name = NULL;
+	GError *error = NULL;
+	NMActStageReturn ret;
+
+	g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (reason !=	NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	if (NM_MODEM_GET_CLASS (self)->get_user_pass) {
+		NMConnection *connection = nm_act_request_get_connection (req);
+
+		g_assert (connection);
+		if (!NM_MODEM_GET_CLASS (self)->get_user_pass (self, connection, &ppp_name, NULL))
+			return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	priv->ppp_manager = nm_ppp_manager_new (priv->iface);
+	if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, 20, &error)) {
+		g_signal_connect (priv->ppp_manager, "state-changed",
+						  G_CALLBACK (ppp_state_changed),
+						  self);
+		g_signal_connect (priv->ppp_manager, "ip4-config",
+						  G_CALLBACK (ppp_ip4_config),
+						  self);
+		g_signal_connect (priv->ppp_manager, "stats",
+						  G_CALLBACK (ppp_stats),
+						  self);
+
+		ret = NM_ACT_STAGE_RETURN_POSTPONE;
+	} else {
+		nm_log_err (LOGD_PPP, "error starting PPP: (%d) %s",
+		            error ? error->code : -1,
+		            error && error->message ? error->message : "(unknown)");
+		g_error_free (error);
+
+		g_object_unref (priv->ppp_manager);
+		priv->ppp_manager = NULL;
+
+		*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
+		ret = NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	return ret;
+}
+
+static NMActStageReturn
+ppp_stage4 (NMModem *self,
+            NMActRequest *req,
+            NMIP4Config **config,
+            NMDeviceStateReason *reason)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+	*config = priv->pending_ip4_config;
+	priv->pending_ip4_config = NULL;
+
+	merge_ip4_config (req, *config);
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+/*****************************************************************************/
+/* IP method static */
+
+static void
+static_stage3_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NMModem *self = NM_MODEM (user_data);
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	GValueArray *ret_array = NULL;
+	GError *error = NULL;
+	NMIP4Config *config = NULL;
+
+	priv->call = NULL;
+
+	if (dbus_g_proxy_end_call (proxy, call, &error,
+							   G_TYPE_VALUE_ARRAY, &ret_array,
+							   G_TYPE_INVALID)) {
+		NMIP4Address *addr;
+		int i;
+
+		config = nm_ip4_config_new ();
+
+		addr = nm_ip4_address_new ();
+		nm_ip4_address_set_address (addr, g_value_get_uint (g_value_array_get_nth (ret_array, 0)));
+		nm_ip4_address_set_prefix (addr, 32);
+		nm_ip4_config_take_address (config, addr);
+
+		for (i = 0; i < ret_array->n_values; i++) {
+			GValue *value = g_value_array_get_nth (ret_array, i);
+
+			nm_ip4_config_add_nameserver (config, g_value_get_uint (value));
+		}
+		g_value_array_free (ret_array);
+
+		priv->pending_ip4_config = g_object_ref (config);
+		g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, NULL, config, NULL);
+	}
+
+	g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, NULL, config, error);
+	g_clear_error (&error);
+}
+
+static NMActStageReturn
+static_stage3_ip4_config_start (NMModem *self,
+                                NMActRequest *req,
+                                NMDeviceStateReason *reason)
+{
+	NMModemPrivate *priv;
+
+	g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (reason !=	NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+
+	priv->call = dbus_g_proxy_begin_call (nm_modem_get_proxy (self, MM_DBUS_INTERFACE_MODEM),
+	                                      "GetIP4Config", static_stage3_done,
+	                                      self, NULL,
+	                                      G_TYPE_INVALID);
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+static NMActStageReturn
+static_stage4 (NMModem *self,
+               NMActRequest *req,
+               NMDevice *device,
+               NMIP4Config **config,
+               NMDeviceStateReason *reason)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	gboolean no_firmware = FALSE;
+
+	if (!nm_device_hw_bring_up (device, TRUE, &no_firmware)) {
+		if (no_firmware)
+			*reason = NM_DEVICE_STATE_REASON_FIRMWARE_MISSING;
+		else
+			*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	*config = priv->pending_ip4_config;
+	priv->pending_ip4_config = NULL;
+
+	merge_ip4_config (req, *config);
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+/*****************************************************************************/
+
+NMActStageReturn
+nm_modem_stage3_ip4_config_start (NMModem *self,
+                                  NMDevice *device,
+                                  NMDeviceClass *device_class,
+                                  NMDeviceStateReason *reason)
+{
+	NMModemPrivate *priv;
+	NMActRequest *req;
+	NMActStageReturn ret;
+
+	g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (device != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (device_class != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	req = nm_device_get_act_request (device);
+	g_assert (req);
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+	switch (priv->ip_method) {
+	case MM_MODEM_IP_METHOD_PPP:
+		ret = ppp_stage3_ip4_config_start (self, req, reason);
+		break;
+	case MM_MODEM_IP_METHOD_STATIC:
+		ret = static_stage3_ip4_config_start (self, req, reason);
+		break;
+	case MM_MODEM_IP_METHOD_DHCP:
+		ret = device_class->act_stage3_ip4_config_start (device, reason);
+		break;
+	default:
+		nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
+		ret = NM_ACT_STAGE_RETURN_FAILURE;
+		break;
+	}
+
+	return ret;
+}
+
+NMActStageReturn
+nm_modem_stage4_get_ip4_config (NMModem *self,
+                                NMDevice *device,
+                                NMDeviceClass *device_class,
+								NMIP4Config **config,
+								NMDeviceStateReason *reason)
+{
+	NMModemPrivate *priv;
+	NMActRequest *req;
+	NMActStageReturn ret;
+
+	g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (device != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (device_class != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	req = nm_device_get_act_request (device);
+	g_assert (req);
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+	switch (priv->ip_method) {
+	case MM_MODEM_IP_METHOD_PPP:
+		ret = ppp_stage4 (self, req, config, reason);
+		break;
+	case MM_MODEM_IP_METHOD_STATIC:
+		ret = static_stage4 (self, req, device, config, reason);
+		break;
+	case MM_MODEM_IP_METHOD_DHCP:
+		ret = device_class->act_stage4_get_ip4_config (device, config, reason);
+		break;
+	default:
+		nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
+		ret = NM_ACT_STAGE_RETURN_FAILURE;
+		break;
+	}
+
+	return ret;
+}
+
+gboolean
+nm_modem_connection_secrets_updated (NMModem *self,
+                                     NMActRequest *req,
+                                     NMConnection *connection,
+                                     GSList *updated_settings,
+                                     RequestSecretsCaller caller)
+{
+	NMModemPrivate *priv;
+	gboolean found = FALSE;
+	const char *setting_name;
+	GSList *iter;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_MODEM (self), FALSE);
+	g_return_val_if_fail (req != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+	g_return_val_if_fail (connection != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+
+	if (caller == SECRETS_CALLER_PPP) {
+		const char *user = NULL;
+		const char *pass = NULL;
+
+		g_return_val_if_fail (priv->ppp_manager != NULL, FALSE);
+
+		if (!NM_MODEM_GET_CLASS (self)->get_user_pass (self, connection, &user, &pass)) {
+			/* Shouldn't ever happen */
+			nm_ppp_manager_update_secrets (priv->ppp_manager,
+										   priv->iface,
+										   NULL,
+										   NULL,
+										   "missing GSM/CDMA setting; no secrets could be found.");
+		} else {
+			nm_ppp_manager_update_secrets (priv->ppp_manager,
+										   priv->iface,
+										   user ? user : "",
+										   pass ? pass : "",
+										   NULL);
+		}
+		return TRUE;
+	}
+
+	g_return_val_if_fail (caller == SECRETS_CALLER_MOBILE_BROADBAND, FALSE);
+
+	g_assert (NM_MODEM_GET_CLASS (self)->get_setting_name);
+	setting_name = NM_MODEM_GET_CLASS (self)->get_setting_name (self);
+
+	for (iter = updated_settings; iter; iter = g_slist_next (iter)) {
+		const char *candidate_setting_name = (const char *) iter->data;
+
+		if (!strcmp (candidate_setting_name, setting_name))
+			found = TRUE;
+		else {
+			nm_log_warn (LOGD_MB, "ignoring updated secrets for setting '%s'.",
+			            candidate_setting_name);
+		}
+	}
+
+	return found;
+}
+
+static NMActStageReturn
+real_act_stage1_prepare (NMModem *modem,
+                         NMActRequest *req,
+                         GPtrArray **out_hints,
+                         const char **out_setting_name,
+                         NMDeviceStateReason *reason)
+{
+	*reason = NM_DEVICE_STATE_REASON_UNKNOWN;
+	return NM_ACT_STAGE_RETURN_FAILURE;
+}
+
+NMActStageReturn
+nm_modem_act_stage1_prepare (NMModem *self,
+                             NMActRequest *req,
+                             NMDeviceStateReason *reason)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	NMActStageReturn ret;
+	GPtrArray *hints = NULL;
+	const char *setting_name = NULL;
+
+	ret = NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self,
+	                                                     req,
+	                                                     &hints,
+	                                                     &setting_name,
+	                                                     reason);
+	if ((ret == NM_ACT_STAGE_RETURN_POSTPONE) && setting_name) {
+		const char *hint1 = NULL, *hint2 = NULL;
+
+		/* Need some secrets */
+		if (hints) {
+			if (hints->len > 0)
+				hint1 = g_ptr_array_index (hints, 0);
+			if (hints->len > 1)
+				hint2 = g_ptr_array_index (hints, 1);
+		}
+
+		g_signal_emit (self, signals[NEED_AUTH], 0,
+		               setting_name, 
+	                   priv->secrets_tries++ ? TRUE : FALSE,
+	                   SECRETS_CALLER_MOBILE_BROADBAND,
+	                   hint1,
+	                   hint2);
+
+		if (hints)
+			g_ptr_array_free (hints, TRUE);
+	}
+	
+	return ret;
+}
+
+NMActStageReturn
+nm_modem_act_stage2_config (NMModem *self,
+                            NMActRequest *req,
+                            NMDeviceStateReason *reason)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+	/* Clear secrets tries counter since secrets were successfully used
+	 * already if we get here.
+	 */
+	priv->secrets_tries = 0;
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+NMConnection *
+nm_modem_get_best_auto_connection (NMModem *self,
+                                   GSList *connections,
+                                   char **specific_object)
+{
+	if (NM_MODEM_GET_CLASS (self)->get_best_auto_connection)
+		return NM_MODEM_GET_CLASS (self)->get_best_auto_connection (self, connections, specific_object);
+	return NULL;
+}
+
+gboolean
+nm_modem_check_connection_compatible (NMModem *self,
+                                      NMConnection *connection,
+                                      GError **error)
+{
+	if (NM_MODEM_GET_CLASS (self)->check_connection_compatible)
+		return NM_MODEM_GET_CLASS (self)->check_connection_compatible (self, connection, error);
+	return FALSE;
+}
+
+static void
+real_deactivate_quickly (NMModem *self, NMDevice *device)
+{
+	NMModemPrivate *priv;
+	const char *iface;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_MODEM (self));
+	g_return_if_fail (device != NULL);
+	g_return_if_fail (NM_IS_DEVICE (device));
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+
+	priv->secrets_tries = 0;
+
+	if (priv->call) {
+		dbus_g_proxy_cancel_call (priv->proxy, priv->call);
+		priv->call = NULL;
+	}
+
+	if (priv->pending_ip4_config) {
+		g_object_unref (priv->pending_ip4_config);
+		priv->pending_ip4_config = NULL;
+	}
+
+	priv->in_bytes = priv->out_bytes = 0;
+
+	switch (priv->ip_method) {
+	case MM_MODEM_IP_METHOD_PPP:
+		if (priv->ppp_manager) {
+			g_object_unref (priv->ppp_manager);
+			priv->ppp_manager = NULL;
+		}
+		break;
+	case MM_MODEM_IP_METHOD_STATIC:
+	case MM_MODEM_IP_METHOD_DHCP:
+		iface = nm_device_get_ip_iface (device);
+		/* FIXME: use AF_UNSPEC here when we have IPv6 support */
+		nm_system_device_flush_routes_with_iface (iface, AF_INET);
+		nm_system_device_flush_addresses_with_iface (iface);
+		nm_system_device_set_up_down_with_iface (iface, FALSE, NULL);
+		break;
+	default:
+		nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
+		break;
+	}
+}
+
+void
+nm_modem_deactivate_quickly (NMModem *self, NMDevice *device)
+{
+	NM_MODEM_GET_CLASS (self)->deactivate_quickly (self, device);
+}
+
+static void
+disconnect_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	GError *error = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) {
+		nm_log_info (LOGD_MB, "disconnect failed: (%d) %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+	}
+}
+
+void
+nm_modem_device_state_changed (NMModem *self,
+                               NMDeviceState new_state,
+                               NMDeviceState old_state,
+                               NMDeviceStateReason reason)
+{
+	gboolean was_connected = FALSE;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_MODEM (self));
+
+	if (IS_ACTIVATING_STATE (old_state) || (old_state == NM_DEVICE_STATE_ACTIVATED))
+		was_connected = TRUE;
+
+	/* Make sure we don't leave the serial device open */
+	switch (new_state) {
+	case NM_DEVICE_STATE_NEED_AUTH:
+		if (NM_MODEM_GET_PRIVATE (self)->ppp_manager)
+			break;
+		/* else fall through */
+	case NM_DEVICE_STATE_UNMANAGED:
+	case NM_DEVICE_STATE_UNAVAILABLE:
+	case NM_DEVICE_STATE_FAILED:
+	case NM_DEVICE_STATE_DISCONNECTED:
+		if (was_connected) {
+			dbus_g_proxy_begin_call (nm_modem_get_proxy (self, MM_DBUS_INTERFACE_MODEM),
+			                         "Disconnect",
+			                         disconnect_done,
+			                         self,
+			                         NULL,
+			                         G_TYPE_INVALID);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+gboolean
+nm_modem_hw_is_up (NMModem *self, NMDevice *device)
+{
+	guint32 ip_method = NM_MODEM_GET_PRIVATE (self)->ip_method;
+
+	if (ip_method == MM_MODEM_IP_METHOD_STATIC || ip_method == MM_MODEM_IP_METHOD_DHCP) {
+		NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+		NMDeviceState state;
+
+		state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device));
+		if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED)
+			return nm_system_device_is_up (device);
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_modem_hw_bring_up (NMModem *self, NMDevice *device, gboolean *no_firmware)
+{
+	guint32 ip_method = NM_MODEM_GET_PRIVATE (self)->ip_method;
+
+	if (ip_method == MM_MODEM_IP_METHOD_STATIC || ip_method == MM_MODEM_IP_METHOD_DHCP) {
+		NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+		NMDeviceState state;
+
+		state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device));
+		if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED)
+			return nm_system_device_set_up_down (device, TRUE, no_firmware);
+	}
+
+	return TRUE;
+}
+
+const char *
+nm_modem_get_iface (NMModem *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_MODEM (self), NULL);
+
+	return NM_MODEM_GET_PRIVATE (self)->iface;
+}
+
+const char *
+nm_modem_get_path (NMModem *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_MODEM (self), NULL);
+
+	return NM_MODEM_GET_PRIVATE (self)->path;
+}
+
+static void
+get_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMModem *self = NM_MODEM (user_data);
+	GError *error = NULL;
+	GValue value = { 0, };
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &error,
+	                            G_TYPE_VALUE, &value,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_MB, "failed get modem enabled state: (%d) %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+		return;
+	}
+
+	if (G_VALUE_HOLDS_BOOLEAN (&value)) {
+		NM_MODEM_GET_PRIVATE (self)->mm_enabled = g_value_get_boolean (&value);
+		g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED);
+	} else
+		nm_log_warn (LOGD_MB, "failed get modem enabled state: unexpected reply type");
+
+	g_value_unset (&value);
+}
+
+static void
+query_mm_enabled (NMModem *self)
+{
+	dbus_g_proxy_begin_call (NM_MODEM_GET_PRIVATE (self)->props_proxy,
+	                         "Get", get_mm_enabled_done,
+	                         self, NULL,
+	                         G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM,
+	                         G_TYPE_STRING, "Enabled",
+	                         G_TYPE_INVALID);
+}
+
+static void
+set_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	GError *error = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_MB, "failed to enable/disable modem: (%d) %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+	}
+
+	/* Update enabled/disabled state again */
+	query_mm_enabled (NM_MODEM (user_data));
+}
+
+void
+nm_modem_set_mm_enabled (NMModem *self, gboolean enabled)
+{
+	NMModemPrivate *priv;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_MODEM (self));
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+
+	/* FIXME: For now this just toggles the ModemManager enabled state.  In the
+	 * future we want to tie this into rfkill state instead so that the user can
+	 * toggle rfkill status of the WWAN modem.
+	 */
+
+	if (priv->mm_enabled != enabled) {
+		DBusGProxy *proxy;
+
+		proxy = nm_modem_get_proxy (self, MM_DBUS_INTERFACE_MODEM);
+		dbus_g_proxy_begin_call (proxy,
+		                         "Enable", set_mm_enabled_done,
+		                         self, NULL,
+		                         G_TYPE_BOOLEAN, enabled,
+		                         G_TYPE_INVALID);
+	}
+}
+
+static void
+modem_properties_changed (DBusGProxy *proxy,
+                          const char *interface,
+                          GHashTable *props,
+                          gpointer user_data)
+{
+	NMModem *self = NM_MODEM (user_data);
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	GValue *value;
+
+	if (strcmp (interface, MM_DBUS_INTERFACE_MODEM))
+		return;
+
+	value = g_hash_table_lookup (props, "Enabled");
+	if (value && G_VALUE_HOLDS_BOOLEAN (value)) {
+		priv->mm_enabled = g_value_get_boolean (value);
+		g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED);
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_modem_init (NMModem *self)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+}
+
+static GObject*
+constructor (GType type,
+			 guint n_construct_params,
+			 GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	NMModemPrivate *priv;
+	DBusGConnection *bus;
+
+	object = G_OBJECT_CLASS (nm_modem_parent_class)->constructor (type,
+																  n_construct_params,
+																  construct_params);
+	if (!object)
+		return NULL;
+
+	priv = NM_MODEM_GET_PRIVATE (object);
+
+	if (!priv->device) {
+		nm_log_err (LOGD_HW, "modem parent device not provided");
+		goto err;
+	}
+
+	if (!priv->device) {
+		nm_log_err (LOGD_HW, "modem command interface not provided");
+		goto err;
+	}
+
+	if (!priv->path) {
+		nm_log_err (LOGD_HW, "D-Bus path not provided");
+		goto err;
+	}
+
+	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	priv->proxy = dbus_g_proxy_new_for_name (bus,
+	                                         MM_DBUS_SERVICE,
+	                                         priv->path,
+	                                         MM_DBUS_INTERFACE_MODEM);
+
+	priv->props_proxy = dbus_g_proxy_new_for_name (bus,
+	                                               MM_DBUS_SERVICE,
+	                                               priv->path,
+	                                               DBUS_INTERFACE_PROPERTIES);
+	dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED,
+	                                   G_TYPE_NONE,
+	                                   G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT,
+	                                   G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->props_proxy, "MmPropertiesChanged",
+	                         G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT,
+	                         G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->props_proxy, "MmPropertiesChanged",
+	                             G_CALLBACK (modem_properties_changed),
+	                             object,
+	                             NULL);
+
+	query_mm_enabled (NM_MODEM (object));
+
+	return object;
+
+ err:
+	g_object_unref (object);
+	return NULL;
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		g_value_set_string (value, priv->path);
+		break;
+	case PROP_DEVICE:
+		g_value_set_string (value, priv->device);
+		break;
+	case PROP_IFACE:
+		g_value_set_string (value, priv->iface);
+		break;
+	case PROP_IP_METHOD:
+		g_value_set_uint (value, priv->ip_method);
+		break;
+	case PROP_ENABLED:
+		g_value_set_boolean (value, priv->mm_enabled);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		/* Construct only */
+		priv->path = g_value_dup_string (value);
+		break;
+	case PROP_DEVICE:
+		/* Construct only */
+		priv->device = g_value_dup_string (value);
+		break;
+	case PROP_IFACE:
+		/* Construct only */
+		priv->iface = g_value_dup_string (value);
+		break;
+	case PROP_IP_METHOD:
+		/* Construct only */
+		priv->ip_method = g_value_get_uint (value);
+		break;
+	case PROP_ENABLED:
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+finalize (GObject *object)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object);
+
+	if (priv->proxy)
+		g_object_unref (priv->proxy);
+
+	if (priv->props_proxy)
+		g_object_unref (priv->props_proxy);
+
+	g_object_unref (priv->dbus_mgr);
+
+	g_free (priv->iface);
+	g_free (priv->path);
+	g_free (priv->device);
+
+	G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object);
+}
+
+static void
+nm_modem_class_init (NMModemClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMModemPrivate));
+
+	/* Virtual methods */
+	object_class->constructor = constructor;
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	klass->act_stage1_prepare = real_act_stage1_prepare;
+	klass->deactivate_quickly = real_deactivate_quickly;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_PATH,
+		 g_param_spec_string (NM_MODEM_PATH,
+							  "DBus path",
+							  "DBus path",
+							  NULL,
+							  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_DEVICE,
+		 g_param_spec_string (NM_MODEM_DEVICE,
+		                      "Device",
+		                      "Master modem parent device",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_IFACE,
+		 g_param_spec_string (NM_MODEM_IFACE,
+		                      "Interface",
+		                      "Modem command interface",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_IP_METHOD,
+		 g_param_spec_uint (NM_MODEM_IP_METHOD,
+							"IP method",
+							"IP method",
+							MM_MODEM_IP_METHOD_PPP,
+							MM_MODEM_IP_METHOD_DHCP,
+							MM_MODEM_IP_METHOD_PPP,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_ENABLED,
+		 g_param_spec_boolean (NM_MODEM_ENABLED,
+		                       "Enabled",
+		                       "Enabled",
+		                       TRUE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	/* Signals */
+	signals[PPP_STATS] =
+		g_signal_new ("ppp-stats",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMModemClass, ppp_stats),
+					  NULL, NULL,
+					  _nm_marshal_VOID__UINT_UINT,
+					  G_TYPE_NONE, 2,
+					  G_TYPE_UINT, G_TYPE_UINT);
+
+	signals[PPP_FAILED] =
+		g_signal_new ("ppp-failed",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMModemClass, ppp_failed),
+					  NULL, NULL,
+					  g_cclosure_marshal_VOID__UINT,
+					  G_TYPE_NONE, 1, G_TYPE_UINT);
+
+	signals[IP4_CONFIG_RESULT] =
+		g_signal_new (NM_MODEM_IP4_CONFIG_RESULT,
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMModemClass, ip4_config_result),
+					  NULL, NULL,
+					  _nm_marshal_VOID__STRING_OBJECT_POINTER,
+					  G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_POINTER);
+
+	signals[PREPARE_RESULT] =
+		g_signal_new (NM_MODEM_PREPARE_RESULT,
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMModemClass, prepare_result),
+					  NULL, NULL,
+					  _nm_marshal_VOID__BOOLEAN_UINT,
+					  G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_UINT);
+
+	signals[NEED_AUTH] =
+		g_signal_new (NM_MODEM_NEED_AUTH,
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMModemClass, need_auth),
+					  NULL, NULL,
+					  _nm_marshal_VOID__STRING_BOOLEAN_UINT_STRING_STRING,
+					  G_TYPE_NONE, 5,
+					  G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
+}
+
+const DBusGObjectInfo *
+nm_modem_get_serial_dbus_info (void)
+{
+	return &dbus_glib_nm_serial_device_object_info;
+}
+
diff --git a/src/modem-manager/nm-modem.h b/src/modem-manager/nm-modem.h
new file mode 100644
index 00000000..a2aed57e
--- /dev/null
+++ b/src/modem-manager/nm-modem.h
@@ -0,0 +1,157 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 Novell, Inc.
+ */
+
+#ifndef NM_MODEM_H
+#define NM_MODEM_H
+
+#include <dbus/dbus-glib.h>
+#include <glib-object.h>
+#include "ppp-manager/nm-ppp-manager.h"
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_MODEM			(nm_modem_get_type ())
+#define NM_MODEM(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM, NMModem))
+#define NM_MODEM_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),	NM_TYPE_MODEM, NMModemClass))
+#define NM_IS_MODEM(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM))
+#define NM_IS_MODEM_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),	NM_TYPE_MODEM))
+#define NM_MODEM_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),	NM_TYPE_MODEM, NMModemClass))
+
+#define NM_MODEM_PATH      "path"
+#define NM_MODEM_DEVICE    "device"
+#define NM_MODEM_IFACE     "iface"
+#define NM_MODEM_IP_METHOD "ip-method"
+#define NM_MODEM_ENABLED   "enabled"
+
+#define NM_MODEM_PPP_STATS         "ppp-stats"
+#define NM_MODEM_PPP_FAILED        "ppp-failed"
+#define NM_MODEM_PREPARE_RESULT    "prepare-result"
+#define NM_MODEM_IP4_CONFIG_RESULT "ip4-config-result"
+#define NM_MODEM_NEED_AUTH         "need-auth"
+
+typedef struct {
+	GObject parent;
+} NMModem;
+
+typedef struct {
+	GObjectClass parent;
+
+	gboolean (*get_user_pass)                  (NMModem *modem,
+	                                            NMConnection *connection,
+	                                            const char **user,
+	                                            const char **pass);
+
+	const char * (*get_setting_name)           (NMModem *modem);
+
+	gboolean (*check_connection_compatible)    (NMModem *modem,
+	                                            NMConnection *connection,
+	                                            GError **error);
+
+	NMConnection * (*get_best_auto_connection) (NMModem *modem,
+	                                            GSList *connections,
+	                                            char **specific_object);
+
+	NMActStageReturn (*act_stage1_prepare)     (NMModem *modem,
+	                                            NMActRequest *req,
+	                                            GPtrArray **out_hints,
+	                                            const char **out_setting_name,
+	                                            NMDeviceStateReason *reason);
+
+	void (*deactivate_quickly)                 (NMModem *self, NMDevice *device);
+
+	/* Signals */
+	void (*ppp_stats)  (NMModem *self, guint32 in_bytes, guint32 out_bytes);
+	void (*ppp_failed) (NMModem *self, NMDeviceStateReason reason);
+
+	void (*prepare_result)    (NMModem *self, gboolean success, NMDeviceStateReason reason);
+	void (*ip4_config_result) (NMModem *self, const char *iface, NMIP4Config *config, GError *error);
+
+	void (*need_auth)  (NMModem *self,
+	                    const char *setting_name,
+	                    gboolean retry,
+	                    RequestSecretsCaller caller,
+	                    const char *hint1,
+	                    const char *hint2);
+} NMModemClass;
+
+GType nm_modem_get_type (void);
+
+/* Protected */
+
+NMPPPManager *nm_modem_get_ppp_manager (NMModem *modem);
+DBusGProxy *  nm_modem_get_proxy       (NMModem *modem, const char *interface);
+const char *  nm_modem_get_iface       (NMModem *modem);
+const char *  nm_modem_get_path        (NMModem *modem);
+
+NMConnection *nm_modem_get_best_auto_connection (NMModem *self,
+                                                 GSList *connections,
+                                                 char **specific_object);
+
+gboolean nm_modem_check_connection_compatible (NMModem *self,
+                                               NMConnection *connection,
+                                               GError **error);
+
+NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem,
+                                              NMActRequest *req,
+                                              NMDeviceStateReason *reason);
+
+NMActStageReturn nm_modem_act_stage2_config (NMModem *modem,
+                                             NMActRequest *req,
+                                             NMDeviceStateReason *reason);
+
+NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem,
+                                                   NMDevice *device,
+                                                   NMDeviceClass *device_class,
+                                                   NMDeviceStateReason *reason);
+
+NMActStageReturn nm_modem_stage4_get_ip4_config (NMModem *modem,
+                                                 NMDevice *device,
+                                                 NMDeviceClass *device_class,
+                                                 NMIP4Config **config,
+                                                 NMDeviceStateReason *reason);
+
+void nm_modem_deactivate_quickly (NMModem *modem, NMDevice *device);
+
+void nm_modem_device_state_changed (NMModem *modem,
+                                    NMDeviceState new_state,
+                                    NMDeviceState old_state,
+                                    NMDeviceStateReason reason);
+
+gboolean nm_modem_hw_is_up (NMModem *modem, NMDevice *device);
+
+gboolean nm_modem_hw_bring_up (NMModem *modem, NMDevice *device, gboolean *no_firmware);
+
+gboolean nm_modem_connection_secrets_updated (NMModem *modem,
+                                              NMActRequest *req,
+                                              NMConnection *connection,
+                                              GSList *updated_settings,
+                                              RequestSecretsCaller caller);
+
+const DBusGObjectInfo *nm_modem_get_serial_dbus_info (void);
+
+gboolean      nm_modem_get_mm_enabled (NMModem *self);
+
+void          nm_modem_set_mm_enabled (NMModem *self, gboolean enabled);
+
+G_END_DECLS
+
+#endif /* NM_MODEM_H */
diff --git a/src/named-manager/Makefile.am b/src/named-manager/Makefile.am
new file mode 100644
index 00000000..a33f7d50
--- /dev/null
+++ b/src/named-manager/Makefile.am
@@ -0,0 +1,21 @@
+INCLUDES = \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/include
+
+noinst_LTLIBRARIES = libnamed-manager.la
+
+libnamed_manager_la_SOURCES = nm-named-manager.h nm-named-manager.c
+
+libnamed_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DNM_PKGDATADIR=\"$(pkgdatadir)\" \
+	-DNM_LOCALSTATEDIR=\"$(localstatedir)\"
+
+libnamed_manager_la_LIBADD = \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
diff --git a/src/named-manager/Makefile.in b/src/named-manager/Makefile.in
new file mode 100644
index 00000000..50fb770a
--- /dev/null
+++ b/src/named-manager/Makefile.in
@@ -0,0 +1,564 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/named-manager
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libnamed_manager_la_DEPENDENCIES =  \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libnamed_manager_la_OBJECTS =  \
+	libnamed_manager_la-nm-named-manager.lo
+libnamed_manager_la_OBJECTS = $(am_libnamed_manager_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libnamed_manager_la_SOURCES)
+DIST_SOURCES = $(libnamed_manager_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/include
+
+noinst_LTLIBRARIES = libnamed-manager.la
+libnamed_manager_la_SOURCES = nm-named-manager.h nm-named-manager.c
+libnamed_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DNM_PKGDATADIR=\"$(pkgdatadir)\" \
+	-DNM_LOCALSTATEDIR=\"$(localstatedir)\"
+
+libnamed_manager_la_LIBADD = \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+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/named-manager/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/named-manager/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libnamed-manager.la: $(libnamed_manager_la_OBJECTS) $(libnamed_manager_la_DEPENDENCIES) 
+	$(LINK)  $(libnamed_manager_la_OBJECTS) $(libnamed_manager_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnamed_manager_la-nm-named-manager.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libnamed_manager_la-nm-named-manager.lo: nm-named-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnamed_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnamed_manager_la-nm-named-manager.lo -MD -MP -MF $(DEPDIR)/libnamed_manager_la-nm-named-manager.Tpo -c -o libnamed_manager_la-nm-named-manager.lo `test -f 'nm-named-manager.c' || echo '$(srcdir)/'`nm-named-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnamed_manager_la-nm-named-manager.Tpo $(DEPDIR)/libnamed_manager_la-nm-named-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-named-manager.c' object='libnamed_manager_la-nm-named-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnamed_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnamed_manager_la-nm-named-manager.lo `test -f 'nm-named-manager.c' || echo '$(srcdir)/'`nm-named-manager.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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-noinstLTLIBRARIES \
+	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: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 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/named-manager/nm-named-manager.c b/src/named-manager/nm-named-manager.c
new file mode 100644
index 00000000..ba89ce42
--- /dev/null
+++ b/src/named-manager/nm-named-manager.c
@@ -0,0 +1,821 @@
+/* -*- 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) 2004 - 2005 Colin Walters <walters@redhat.com>
+ * Copyright (C) 2004 - 2010 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ *   and others
+ */
+
+#include "config.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/wait.h> 
+#include <unistd.h>
+#include <glib.h>
+
+#include <glib/gi18n.h>
+
+#include "nm-named-manager.h"
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
+#include "nm-logging.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#ifndef RESOLV_CONF
+#define RESOLV_CONF "/etc/resolv.conf"
+#endif
+
+#define ADDR_BUF_LEN 50
+
+G_DEFINE_TYPE(NMNamedManager, nm_named_manager, G_TYPE_OBJECT)
+
+#define NM_NAMED_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                         NM_TYPE_NAMED_MANAGER, \
+                                         NMNamedManagerPrivate))
+
+
+struct NMNamedManagerPrivate {
+	NMIP4Config *ip4_vpn_config;
+	NMIP4Config *ip4_device_config;
+	NMIP6Config *ip6_vpn_config;
+	NMIP6Config *ip6_device_config;
+	GSList *configs;
+};
+
+
+NMNamedManager *
+nm_named_manager_get (void)
+{
+	static NMNamedManager * singleton = NULL;
+
+	if (!singleton)
+		singleton = NM_NAMED_MANAGER (g_object_new (NM_TYPE_NAMED_MANAGER, NULL));
+	else
+		g_object_ref (singleton);
+
+	g_assert (singleton);
+	return singleton;
+}
+
+
+GQuark
+nm_named_manager_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm_named_manager_error");
+
+	return quark;
+}
+
+typedef struct {
+	GPtrArray *nameservers;
+	const char *domain;
+	GPtrArray *searches;
+	const char *nis_domain;
+	GPtrArray *nis_servers;
+} NMResolvConfData;
+
+static void
+add_string_item (GPtrArray *array, const char *str)
+{
+	int i;
+
+	g_return_if_fail (array != NULL);
+	g_return_if_fail (str != NULL);
+
+	/* Check for dupes before adding */
+	for (i = 0; i < array->len; i++) {
+		const char *candidate = g_ptr_array_index (array, i);
+
+		if (candidate && !strcmp (candidate, str))
+			return;
+	}
+
+	/* No dupes, add the new item */
+	g_ptr_array_add (array, g_strdup (str));
+}
+
+static void
+merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
+{
+	guint32 num, i;
+
+	num = nm_ip4_config_get_num_nameservers (src);
+	for (i = 0; i < num; i++) {
+		struct in_addr addr;
+		char buf[INET_ADDRSTRLEN];
+
+		addr.s_addr = nm_ip4_config_get_nameserver (src, i);
+		if (inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN) > 0)
+			add_string_item (rc->nameservers, buf);
+	}
+
+	num = nm_ip4_config_get_num_domains (src);
+	for (i = 0; i < num; i++) {
+		const char *domain;
+
+		domain = nm_ip4_config_get_domain (src, i);
+		if (!rc->domain)
+			rc->domain = domain;
+		add_string_item (rc->searches, domain);
+	}
+
+	num = nm_ip4_config_get_num_searches (src);
+	for (i = 0; i < num; i++)
+		add_string_item (rc->searches, nm_ip4_config_get_search (src, i));
+
+	/* NIS stuff */
+	num = nm_ip4_config_get_num_nis_servers (src);
+	for (i = 0; i < num; i++) {
+		struct in_addr addr;
+		char buf[INET_ADDRSTRLEN];
+
+		addr.s_addr = nm_ip4_config_get_nis_server (src, i);
+		if (inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN) > 0)
+			add_string_item (rc->nis_servers, buf);
+	}
+
+	if (nm_ip4_config_get_nis_domain (src)) {
+		/* FIXME: handle multiple domains */
+		if (!rc->nis_domain)
+			rc->nis_domain = nm_ip4_config_get_nis_domain (src);
+	}
+}
+
+static void
+merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src)
+{
+	guint32 num, i;
+
+	num = nm_ip6_config_get_num_nameservers (src);
+	for (i = 0; i < num; i++) {
+		const struct in6_addr *addr;
+		char buf[INET6_ADDRSTRLEN];
+
+		addr = nm_ip6_config_get_nameserver (src, i);
+
+		/* inet_ntop is probably supposed to do this for us, but it doesn't */
+		if (IN6_IS_ADDR_V4MAPPED (addr)) {
+			if (inet_ntop (AF_INET, &(addr->s6_addr32[3]), buf, INET_ADDRSTRLEN) > 0)
+				add_string_item (rc->nameservers, buf);
+		} else {
+			if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0)
+				add_string_item (rc->nameservers, buf);
+		}
+	}
+
+	num = nm_ip6_config_get_num_domains (src);
+	for (i = 0; i < num; i++) {
+		const char *domain;
+
+		domain = nm_ip6_config_get_domain (src, i);
+		if (!rc->domain)
+			rc->domain = domain;
+		add_string_item (rc->searches, domain);
+	}
+
+	num = nm_ip6_config_get_num_searches (src);
+	for (i = 0; i < num; i++)
+		add_string_item (rc->searches, nm_ip6_config_get_search (src, i));
+}
+
+
+#if defined(TARGET_SUSE)
+/**********************************/
+/* SUSE */
+
+static void
+netconfig_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static GPid
+run_netconfig (GError **error, gint *stdin_fd)
+{
+	char *argv[5];
+	char *tmp;
+	GPid pid = -1;
+
+	argv[0] = "/sbin/netconfig";
+	argv[1] = "modify";
+	argv[2] = "--service";
+	argv[3] = "NetworkManager";
+	argv[4] = NULL;
+
+	tmp = g_strjoinv (" ", argv);
+	nm_log_dbg (LOGD_DNS, "spawning '%s'", tmp);
+	g_free (tmp);
+
+	if (!g_spawn_async_with_pipes (NULL, argv, NULL, 0, netconfig_child_setup,
+	                               NULL, &pid, stdin_fd, NULL, NULL, error))
+		return -1;
+
+	return pid;
+}
+
+static void
+write_to_netconfig (gint fd, const char *key, const char *value)
+{
+	char *str;
+	int x;
+
+	str = g_strdup_printf ("%s='%s'\n", key, value);
+	nm_log_dbg (LOGD_DNS, "writing to netconfig: %s", str);
+	x = write (fd, str, strlen (str));
+	g_free (str);
+}
+
+static gboolean
+dispatch_netconfig (const char *domain,
+                    char **searches,
+                    char **nameservers,
+                    const char *nis_domain,
+                    char **nis_servers,
+                    const char *iface,
+                    GError **error)
+{
+	char *str;
+	GPid pid;
+	gint fd;
+	int ret;
+
+	pid = run_netconfig (error, &fd);
+	if (pid < 0)
+		return FALSE;
+
+	// FIXME: this is wrong. We are not writing out the iface-specific
+	// resolv.conf data, we are writing out an already-fully-merged
+	// resolv.conf. Assuming netconfig works in the obvious way, then
+	// there are various failure modes, such as, eg, bringing up a VPN on
+	// eth0, then bringing up wlan0, then bringing down the VPN. Because
+	// NMNamedManager would have claimed that the VPN DNS server was also
+	// part of the wlan0 config, it will remain in resolv.conf after the
+	// VPN goes down, even though it is presumably no longer reachable
+	// at that point.
+	write_to_netconfig (fd, "INTERFACE", iface);
+
+	if (searches) {
+		str = g_strjoinv (" ", searches);
+
+		if (domain) {
+			char *tmp;
+
+			tmp = g_strconcat (domain, " ", str, NULL);
+			g_free (str);
+			str = tmp;
+		}
+
+		write_to_netconfig (fd, "DNSSEARCH", str);
+		g_free (str);
+	}
+
+	if (nameservers) {
+		str = g_strjoinv (" ", nameservers);
+		write_to_netconfig (fd, "DNSSERVERS", str);
+		g_free (str);
+	}
+
+	if (nis_domain)
+		write_to_netconfig (fd, "NISDOMAIN", nis_domain);
+
+	if (nis_servers) {
+		str = g_strjoinv (" ", nis_servers);
+		write_to_netconfig (fd, "NISSERVERS", str);
+		g_free (str);
+	}
+
+	close (fd);
+
+	/* Wait until the process exits */
+
+ again:
+
+	ret = waitpid (pid, NULL, 0);
+	if (ret < 0 && errno == EINTR)
+		goto again;
+	else if (ret < 0 && errno == ECHILD) {
+		/* When the netconfig exist, the errno is ECHILD, it should return TRUE */
+		return TRUE;
+	}
+
+	return ret > 0;
+}
+#endif
+
+
+static gboolean
+write_resolv_conf (FILE *f, const char *domain,
+                   char **searches,
+                   char **nameservers,
+                   GError **error)
+{
+	char *domain_str = NULL;
+	char *searches_str = NULL;
+	char *nameservers_str = NULL;
+	int i;
+	gboolean retval = FALSE;
+
+	if (fprintf (f, "%s","# Generated by NetworkManager\n") < 0) {
+		g_set_error (error,
+				   NM_NAMED_MANAGER_ERROR,
+				   NM_NAMED_MANAGER_ERROR_SYSTEM,
+				   "Could not write " RESOLV_CONF ": %s\n",
+				   g_strerror (errno));
+		return FALSE;
+	}
+
+	if (domain)
+		domain_str = g_strconcat ("domain ", domain, "\n", NULL);
+
+	if (searches) {
+		char *tmp_str;
+
+		tmp_str = g_strjoinv (" ", searches);
+		searches_str = g_strconcat ("search ", tmp_str, "\n", NULL);
+		g_free (tmp_str);
+	}
+
+	if (nameservers) {
+		GString *str;
+		int num;
+
+		str = g_string_new ("");
+		num = g_strv_length (nameservers);
+
+		for (i = 0; i < num; i++) {
+			if (i == 3) {
+				g_string_append (str, "# ");
+				g_string_append (str, _("NOTE: the libc resolver may not support more than 3 nameservers."));
+				g_string_append (str, "\n# ");
+				g_string_append (str, _("The nameservers listed below may not be recognized."));
+				g_string_append_c (str, '\n');
+			}
+
+			g_string_append (str, "nameserver ");
+			g_string_append (str, nameservers[i]);
+			g_string_append_c (str, '\n');
+		}
+
+		nameservers_str = g_string_free (str, FALSE);
+	}
+
+	if (fprintf (f, "%s%s%s",
+	             domain_str ? domain_str : "",
+	             searches_str ? searches_str : "",
+	             nameservers_str ? nameservers_str : "") != -1)
+		retval = TRUE;
+
+	g_free (domain_str);
+	g_free (searches_str);
+	g_free (nameservers_str);
+
+	return retval;
+}
+
+#ifdef RESOLVCONF_PATH
+static gboolean
+dispatch_resolvconf (const char *domain,
+                     char **searches,
+                     char **nameservers,
+                     const char *iface,
+                     GError **error)
+{
+	char *cmd;
+	FILE *f;
+	gboolean retval = FALSE;
+
+	if (! g_file_test (RESOLVCONF_PATH, G_FILE_TEST_IS_EXECUTABLE))
+		return FALSE;
+
+	if (domain || searches || nameservers) {
+		cmd = g_strconcat (RESOLVCONF_PATH, " -a ", "NetworkManager", NULL);
+		nm_log_info (LOGD_DNS, "(%s): writing resolv.conf to %s", iface, RESOLVCONF_PATH);
+		if ((f = popen (cmd, "w")) == NULL)
+			g_set_error (error,
+				     NM_NAMED_MANAGER_ERROR,
+				     NM_NAMED_MANAGER_ERROR_SYSTEM,
+				     "Could not write to %s: %s\n",
+				     RESOLVCONF_PATH,
+				     g_strerror (errno));
+		else {
+			retval = write_resolv_conf (f, domain, searches, nameservers, error);
+			retval &= (pclose (f) == 0);
+		}
+	} else {
+		cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL);
+		nm_log_info (LOGD_DNS, "(%s): removing resolv.conf from %s", iface, RESOLVCONF_PATH);
+		if (nm_spawn_process (cmd) == 0)
+			retval = TRUE;
+	}
+
+	g_free (cmd);
+
+	return retval;
+}
+#endif
+
+static gboolean
+update_resolv_conf (const char *domain,
+                    char **searches,
+                    char **nameservers,
+                    const char *iface,
+                    GError **error)
+{
+	char *tmp_resolv_conf;
+	char *tmp_resolv_conf_realpath;
+	char *resolv_conf_realpath;
+	FILE *f;
+	int do_rename = 1;
+	int old_errno = 0;
+
+	g_return_val_if_fail (error != NULL, FALSE);
+
+	/* Find the real path of resolv.conf; it could be a symlink to something */
+	resolv_conf_realpath = realpath (RESOLV_CONF, NULL);
+	if (!resolv_conf_realpath)
+		resolv_conf_realpath = strdup (RESOLV_CONF);
+
+	/* Build up the real path for the temp resolv.conf that we're about to
+	 * write out.
+	 */
+	tmp_resolv_conf = g_strdup_printf ("%s.tmp", resolv_conf_realpath);
+	tmp_resolv_conf_realpath = realpath (tmp_resolv_conf, NULL);
+	if (!tmp_resolv_conf_realpath)
+		tmp_resolv_conf_realpath = strdup (tmp_resolv_conf);
+	g_free (tmp_resolv_conf);
+	tmp_resolv_conf = NULL;
+
+	if ((f = fopen (tmp_resolv_conf_realpath, "w")) == NULL) {
+		do_rename = 0;
+		old_errno = errno;
+		if ((f = fopen (RESOLV_CONF, "w")) == NULL) {
+			g_set_error (error,
+			             NM_NAMED_MANAGER_ERROR,
+			             NM_NAMED_MANAGER_ERROR_SYSTEM,
+			             "Could not open %s: %s\nCould not open %s: %s\n",
+			             tmp_resolv_conf_realpath,
+			             g_strerror (old_errno),
+			             RESOLV_CONF,
+			             g_strerror (errno));
+			goto out;
+		}
+		/* Update tmp_resolv_conf_realpath so the error message on fclose()
+		 * failure will be correct.
+		 */
+		strcpy (tmp_resolv_conf_realpath, RESOLV_CONF);
+	}
+
+	write_resolv_conf (f, domain, searches, nameservers, error);
+
+	if (fclose (f) < 0) {
+		if (*error == NULL) {
+			/* only set an error here if write_resolv_conf() was successful,
+			 * since its error is more important.
+			 */
+			g_set_error (error,
+			             NM_NAMED_MANAGER_ERROR,
+			             NM_NAMED_MANAGER_ERROR_SYSTEM,
+			             "Could not close %s: %s\n",
+			             tmp_resolv_conf_realpath,
+			             g_strerror (errno));
+		}
+	}
+
+	/* Don't rename the tempfile over top of the existing resolv.conf if there
+	 * was an error writing it out.
+	 */
+	if (*error == NULL && do_rename) {
+		if (rename (tmp_resolv_conf_realpath, resolv_conf_realpath) < 0) {
+			g_set_error (error,
+			             NM_NAMED_MANAGER_ERROR,
+			             NM_NAMED_MANAGER_ERROR_SYSTEM,
+			             "Could not replace " RESOLV_CONF ": %s\n",
+			             g_strerror (errno));
+		}
+	}
+
+out:
+	free (tmp_resolv_conf_realpath);
+	free (resolv_conf_realpath);
+	return *error ? FALSE : TRUE;
+}
+
+static gboolean
+rewrite_resolv_conf (NMNamedManager *mgr, const char *iface, GError **error)
+{
+	NMNamedManagerPrivate *priv;
+	NMResolvConfData rc;
+	GSList *iter;
+	const char *domain = NULL;
+	const char *nis_domain = NULL;
+	char **searches = NULL;
+	char **nameservers = NULL;
+	char **nis_servers = NULL;
+	int num, i, len;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+
+	rc.nameservers = g_ptr_array_new ();
+	rc.domain = NULL;
+	rc.searches = g_ptr_array_new ();
+	rc.nis_servers = g_ptr_array_new ();
+
+	if (priv->ip4_vpn_config)
+		merge_one_ip4_config (&rc, priv->ip4_vpn_config);
+	if (priv->ip4_device_config)
+		merge_one_ip4_config (&rc, priv->ip4_device_config);
+
+	if (priv->ip6_vpn_config)
+		merge_one_ip6_config (&rc, priv->ip6_vpn_config);
+	if (priv->ip6_device_config)
+		merge_one_ip6_config (&rc, priv->ip6_device_config);
+
+	for (iter = priv->configs; iter; iter = g_slist_next (iter)) {
+		if (   (iter->data == priv->ip4_vpn_config)
+		    || (iter->data == priv->ip4_device_config)
+		    || (iter->data == priv->ip6_vpn_config)
+		    || (iter->data == priv->ip6_device_config))
+			continue;
+
+		if (NM_IS_IP4_CONFIG (iter->data)) {
+			NMIP4Config *config = NM_IP4_CONFIG (iter->data);
+
+			merge_one_ip4_config (&rc, config);
+		} else if (NM_IS_IP6_CONFIG (iter->data)) {
+			NMIP6Config *config = NM_IP6_CONFIG (iter->data);
+
+			merge_one_ip6_config (&rc, config);
+		} else
+			g_assert_not_reached ();
+	}
+
+	domain = rc.domain;
+
+	/* Per 'man resolv.conf', the search list is limited to 6 domains
+	 * totalling 256 characters.
+	 */
+	num = MIN (rc.searches->len, 6);
+	for (i = 0, len = 0; i < num; i++) {
+		len += strlen (rc.searches->pdata[i]) + 1; /* +1 for spaces */
+		if (len > 256)
+			break;
+	}
+	g_ptr_array_set_size (rc.searches, i);
+	if (rc.searches->len) {
+		g_ptr_array_add (rc.searches, NULL);
+		searches = (char **) g_ptr_array_free (rc.searches, FALSE);
+	} else
+		g_ptr_array_free (rc.searches, TRUE);
+
+	if (rc.nameservers->len) {
+		g_ptr_array_add (rc.nameservers, NULL);
+		nameservers = (char **) g_ptr_array_free (rc.nameservers, FALSE);
+	} else
+		g_ptr_array_free (rc.nameservers, TRUE);
+
+	if (rc.nis_servers->len) {
+		g_ptr_array_add (rc.nis_servers, NULL);
+		nis_servers = (char **) g_ptr_array_free (rc.nis_servers, FALSE);
+	} else
+		g_ptr_array_free (rc.nis_servers, TRUE);
+
+	nis_domain = rc.nis_domain;
+
+#ifdef RESOLVCONF_PATH
+	success = dispatch_resolvconf (domain, searches, nameservers, iface, error);
+#endif
+
+#ifdef TARGET_SUSE
+	if (success == FALSE) {
+		success = dispatch_netconfig (domain, searches, nameservers,
+		                              nis_domain, nis_servers,
+		                              iface, error);
+	}
+#endif
+
+	if (success == FALSE)
+		success = update_resolv_conf (domain, searches, nameservers, iface, error);
+
+	if (success)
+		nm_system_update_dns ();
+
+	if (searches)
+		g_strfreev (searches);
+	if (nameservers)
+		g_strfreev (nameservers);
+	if (nis_servers)
+		g_strfreev (nis_servers);
+
+	return success;
+}
+
+gboolean
+nm_named_manager_add_ip4_config (NMNamedManager *mgr,
+								 const char *iface,
+								 NMIP4Config *config,
+								 NMNamedIPConfigType cfg_type)
+{
+	NMNamedManagerPrivate *priv;
+	GError *error = NULL;
+
+	g_return_val_if_fail (mgr != NULL, FALSE);
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (config != NULL, FALSE);
+
+	priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+
+	switch (cfg_type) {
+	case NM_NAMED_IP_CONFIG_TYPE_VPN:
+		priv->ip4_vpn_config = config;
+		break;
+	case NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE:
+		priv->ip4_device_config = config;
+		break;
+	default:
+		break;
+	}
+
+	/* Don't allow the same zone added twice */
+	if (!g_slist_find (priv->configs, config))
+		priv->configs = g_slist_append (priv->configs, g_object_ref (config));
+
+	if (!rewrite_resolv_conf (mgr, iface, &error)) {
+		nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)");
+		g_error_free (error);
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_named_manager_remove_ip4_config (NMNamedManager *mgr,
+									const char *iface,
+									NMIP4Config *config)
+{
+	NMNamedManagerPrivate *priv;
+	GError *error = NULL;
+
+	g_return_val_if_fail (mgr != NULL, FALSE);
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (config != NULL, FALSE);
+
+	priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+
+	/* Can't remove it if it wasn't in the list to begin with */
+	if (!g_slist_find (priv->configs, config))
+		return FALSE;
+
+	priv->configs = g_slist_remove (priv->configs, config);
+
+	if (config == priv->ip4_vpn_config)
+		priv->ip4_vpn_config = NULL;
+	if (config == priv->ip4_device_config)
+		priv->ip4_device_config = NULL;
+
+	g_object_unref (config);
+
+	if (!rewrite_resolv_conf (mgr, iface, &error)) {
+		nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)");
+		if (error)
+			g_error_free (error);
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_named_manager_add_ip6_config (NMNamedManager *mgr,
+								 const char *iface,
+								 NMIP6Config *config,
+								 NMNamedIPConfigType cfg_type)
+{
+	NMNamedManagerPrivate *priv;
+	GError *error = NULL;
+
+	g_return_val_if_fail (mgr != NULL, FALSE);
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (config != NULL, FALSE);
+
+	priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+
+	switch (cfg_type) {
+	case NM_NAMED_IP_CONFIG_TYPE_VPN:
+		/* FIXME: not quite yet... */
+		g_return_val_if_fail (cfg_type != NM_NAMED_IP_CONFIG_TYPE_VPN, FALSE);
+		priv->ip6_vpn_config = config;
+		break;
+	case NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE:
+		priv->ip6_device_config = config;
+		break;
+	default:
+		break;
+	}
+
+	/* Don't allow the same zone added twice */
+	if (!g_slist_find (priv->configs, config))
+		priv->configs = g_slist_append (priv->configs, g_object_ref (config));
+
+	if (!rewrite_resolv_conf (mgr, iface, &error)) {
+		nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)");
+		g_error_free (error);
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_named_manager_remove_ip6_config (NMNamedManager *mgr,
+									const char *iface,
+									NMIP6Config *config)
+{
+	NMNamedManagerPrivate *priv;
+	GError *error = NULL;
+
+	g_return_val_if_fail (mgr != NULL, FALSE);
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (config != NULL, FALSE);
+
+	priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+
+	/* Can't remove it if it wasn't in the list to begin with */
+	if (!g_slist_find (priv->configs, config))
+		return FALSE;
+
+	priv->configs = g_slist_remove (priv->configs, config);
+
+	if (config == priv->ip6_vpn_config)
+		priv->ip6_vpn_config = NULL;
+	if (config == priv->ip6_device_config)
+		priv->ip6_device_config = NULL;
+
+	g_object_unref (config);	
+
+	if (!rewrite_resolv_conf (mgr, iface, &error)) {
+		nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)");
+		if (error)
+			g_error_free (error);
+	}
+
+	return TRUE;
+}
+
+
+static void
+nm_named_manager_init (NMNamedManager *mgr)
+{
+}
+
+static void
+nm_named_manager_finalize (GObject *object)
+{
+	NMNamedManagerPrivate *priv = NM_NAMED_MANAGER_GET_PRIVATE (object);
+
+	g_slist_foreach (priv->configs, (GFunc) g_object_unref, NULL);
+	g_slist_free (priv->configs);
+
+	G_OBJECT_CLASS (nm_named_manager_parent_class)->finalize (object);
+}
+
+static void
+nm_named_manager_class_init (NMNamedManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = nm_named_manager_finalize;
+
+	g_type_class_add_private (object_class, sizeof (NMNamedManagerPrivate));
+}
+
diff --git a/src/named-manager/nm-named-manager.h b/src/named-manager/nm-named-manager.h
new file mode 100644
index 00000000..47c8e711
--- /dev/null
+++ b/src/named-manager/nm-named-manager.h
@@ -0,0 +1,92 @@
+/* -*- 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) 2004 - 2005 Colin Walters <walters@redhat.com>
+ * Copyright (C) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ *   and others
+ */
+
+#ifndef __NM_NAMED_MANAGER_H__
+#define __NM_NAMED_MANAGER_H__
+
+#include "config.h"
+#include <glib-object.h>
+#include <dbus/dbus.h>
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
+
+typedef enum {
+	NM_NAMED_MANAGER_ERROR_SYSTEM,
+	NM_NAMED_MANAGER_ERROR_INVALID_NAMESERVER,
+	NM_NAMED_MANAGER_ERROR_INVALID_HOST,
+	NM_NAMED_MANAGER_ERROR_INVALID_ID
+} NMNamedManagerError;
+
+typedef enum {
+	NM_NAMED_IP_CONFIG_TYPE_DEFAULT = 0,
+	NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE,
+	NM_NAMED_IP_CONFIG_TYPE_VPN
+} NMNamedIPConfigType;
+
+#define NM_NAMED_MANAGER_ERROR nm_named_manager_error_quark ()
+GQuark nm_named_manager_error_quark (void);
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_NAMED_MANAGER (nm_named_manager_get_type ())
+#define NM_NAMED_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_NAMED_MANAGER, NMNamedManager))
+#define NM_NAMED_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_NAMED_MANAGER, NMNamedManagerClass))
+#define NM_IS_NAMED_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_NAMED_MANAGER))
+#define NM_IS_NAMED_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_NAMED_MANAGER))
+#define NM_NAMED_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_NAMED_MANAGER, NMNamedManagerClass)) 
+
+typedef struct NMNamedManagerPrivate NMNamedManagerPrivate;
+
+typedef struct {
+	GObject parent;
+} NMNamedManager;
+
+typedef struct {
+	GObjectClass parent;
+} NMNamedManagerClass;
+
+GType nm_named_manager_get_type (void);
+
+NMNamedManager * nm_named_manager_get (void);
+
+gboolean nm_named_manager_add_ip4_config (NMNamedManager *mgr,
+										  const char *iface,
+                                          NMIP4Config *config,
+                                          NMNamedIPConfigType cfg_type);
+
+gboolean nm_named_manager_remove_ip4_config (NMNamedManager *mgr,
+											 const char *iface,
+											 NMIP4Config *config);
+
+gboolean nm_named_manager_add_ip6_config (NMNamedManager *mgr,
+										  const char *iface,
+                                          NMIP6Config *config,
+                                          NMNamedIPConfigType cfg_type);
+
+gboolean nm_named_manager_remove_ip6_config (NMNamedManager *mgr,
+											 const char *iface,
+											 NMIP6Config *config);
+
+G_END_DECLS
+
+#endif /* __NM_NAMED_MANAGER_H__ */
diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c
new file mode 100644
index 00000000..2529e77f
--- /dev/null
+++ b/src/nm-activation-request.c
@@ -0,0 +1,700 @@
+/* -*- 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 - 2010 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dbus/dbus-glib.h>
+
+#include "nm-activation-request.h"
+#include "nm-marshal.h"
+#include "nm-logging.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
+#include "nm-dbus-manager.h"
+#include "nm-device.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-active-connection.h"
+#include "nm-dbus-glib-types.h"
+
+
+static void secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class);
+
+G_DEFINE_TYPE_EXTENDED (NMActRequest, nm_act_request, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_SECRETS_PROVIDER_INTERFACE,
+                                               secrets_provider_interface_init))
+
+#define NM_ACT_REQUEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ACT_REQUEST, NMActRequestPrivate))
+
+enum {
+	CONNECTION_SECRETS_UPDATED,
+	CONNECTION_SECRETS_FAILED,
+	PROPERTIES_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct {
+	char *table;
+	char *rule;
+} ShareRule;
+
+typedef struct {
+	gboolean disposed;
+
+	NMConnection *connection;
+	guint32 secrets_call_id;
+
+	char *specific_object;
+	NMDevice *device;
+	gboolean user_requested;
+
+	NMActiveConnectionState state;
+	gboolean is_default;
+	gboolean is_default6;
+	gboolean shared;
+	GSList *share_rules;
+
+	char *ac_path;
+
+	gboolean assumed;
+} NMActRequestPrivate;
+
+enum {
+	PROP_0,
+	PROP_SERVICE_NAME,
+	PROP_CONNECTION,
+	PROP_SPECIFIC_OBJECT,
+	PROP_DEVICES,
+	PROP_STATE,
+	PROP_DEFAULT,
+	PROP_DEFAULT6,
+	PROP_VPN,
+
+	LAST_PROP
+};
+
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMActRequest *self = NM_ACT_REQUEST (user_data);
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self);
+	NMActiveConnectionState new_ac_state;
+	gboolean new_default = FALSE, new_default6 = FALSE;
+
+	/* Set NMActiveConnection state based on the device's state */
+	switch (new_state) {
+	case NM_DEVICE_STATE_PREPARE:
+	case NM_DEVICE_STATE_CONFIG:
+	case NM_DEVICE_STATE_NEED_AUTH:
+	case NM_DEVICE_STATE_IP_CONFIG:
+		new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATING;
+		break;
+	case NM_DEVICE_STATE_ACTIVATED:
+		new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATED;
+		new_default = priv->is_default;
+		new_default6 = priv->is_default6;
+		break;
+	default:
+		new_ac_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
+		break;
+	}
+
+	if (new_ac_state != priv->state) {
+		priv->state = new_ac_state;
+		g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_STATE);
+	}
+
+	if (new_default != priv->is_default) {
+		priv->is_default = new_default;
+		g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEFAULT);
+	}
+
+	if (new_default6 != priv->is_default6) {
+		priv->is_default6 = new_default6;
+		g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEFAULT6);
+	}
+}
+
+NMActRequest *
+nm_act_request_new (NMConnection *connection,
+                    const char *specific_object,
+                    gboolean user_requested,
+                    gboolean assumed,
+                    gpointer *device)
+{
+	GObject *object;
+	NMActRequestPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_DEVICE (device), NULL);
+
+	object = g_object_new (NM_TYPE_ACT_REQUEST, NULL);
+	if (!object)
+		return NULL;
+
+	priv = NM_ACT_REQUEST_GET_PRIVATE (object);
+
+	priv->connection = g_object_ref (connection);
+	if (specific_object)
+		priv->specific_object = g_strdup (specific_object);
+
+	priv->device = NM_DEVICE (device);
+	g_signal_connect (device, "state-changed",
+	                  G_CALLBACK (device_state_changed),
+	                  NM_ACT_REQUEST (object));
+
+	priv->user_requested = user_requested;
+	priv->assumed = assumed;
+
+	return NM_ACT_REQUEST (object);
+}
+
+static void
+nm_act_request_init (NMActRequest *req)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+	NMDBusManager *dbus_mgr;
+
+	priv->ac_path = nm_active_connection_get_next_object_path ();
+	priv->state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
+
+	dbus_mgr = nm_dbus_manager_get ();
+	dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (dbus_mgr),
+	                                     priv->ac_path,
+	                                     G_OBJECT (req));
+	g_object_unref (dbus_mgr);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object);
+
+	if (priv->disposed) {
+		G_OBJECT_CLASS (nm_act_request_parent_class)->dispose (object);
+		return;
+	}
+	priv->disposed = TRUE;
+
+	g_assert (priv->connection);
+
+	g_signal_handlers_disconnect_by_func (G_OBJECT (priv->device),
+	                                      G_CALLBACK (device_state_changed),
+	                                      NM_ACT_REQUEST (object));
+
+	/* Clear any share rules */
+	nm_act_request_set_shared (NM_ACT_REQUEST (object), FALSE);
+
+	g_object_unref (priv->connection);
+
+	G_OBJECT_CLASS (nm_act_request_parent_class)->dispose (object);
+}
+
+static void
+clear_share_rules (NMActRequest *req)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+	GSList *iter;
+
+	for (iter = priv->share_rules; iter; iter = g_slist_next (iter)) {
+		ShareRule *rule = (ShareRule *) iter->data;
+
+		g_free (rule->table);
+		g_free (rule->rule);
+		g_free (rule);
+	}
+
+	g_slist_free (priv->share_rules);
+	priv->share_rules = NULL;
+}
+
+static void
+finalize (GObject *object)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object);
+
+	g_free (priv->specific_object);
+	g_free (priv->ac_path);
+
+	clear_share_rules (NM_ACT_REQUEST (object));
+
+	G_OBJECT_CLASS (nm_act_request_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (object);
+	GPtrArray *devices;
+
+	switch (prop_id) {
+	case PROP_SERVICE_NAME:
+		nm_active_connection_scope_to_value (priv->connection, value);
+		break;
+	case PROP_CONNECTION:
+		g_value_set_boxed (value, nm_connection_get_path (priv->connection));
+		break;
+	case PROP_SPECIFIC_OBJECT:
+		if (priv->specific_object)
+			g_value_set_boxed (value, priv->specific_object);
+		else
+			g_value_set_boxed (value, "/");
+		break;
+	case PROP_DEVICES:
+		devices = g_ptr_array_sized_new (1);
+		g_ptr_array_add (devices, g_strdup (nm_device_get_path (priv->device)));
+		g_value_take_boxed (value, devices);
+		break;
+	case PROP_STATE:
+		g_value_set_uint (value, priv->state);
+		break;
+	case PROP_DEFAULT:
+		g_value_set_boolean (value, priv->is_default);
+		break;
+	case PROP_DEFAULT6:
+		g_value_set_boolean (value, priv->is_default6);
+		break;
+	case PROP_VPN:
+		g_value_set_boolean (value, FALSE);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_act_request_class_init (NMActRequestClass *req_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+	g_type_class_add_private (req_class, sizeof (NMActRequestPrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_SERVICE_NAME,
+		 g_param_spec_string (NM_ACTIVE_CONNECTION_SERVICE_NAME,
+							  "Service name",
+							  "Service name",
+							  NULL,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_CONNECTION,
+		 g_param_spec_boxed (NM_ACTIVE_CONNECTION_CONNECTION,
+							  "Connection",
+							  "Connection",
+							  DBUS_TYPE_G_OBJECT_PATH,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_SPECIFIC_OBJECT,
+		 g_param_spec_boxed (NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT,
+							  "Specific object",
+							  "Specific object",
+							  DBUS_TYPE_G_OBJECT_PATH,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_DEVICES,
+		 g_param_spec_boxed (NM_ACTIVE_CONNECTION_DEVICES,
+							  "Devices",
+							  "Devices",
+							  DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_STATE,
+		 g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE,
+							  "State",
+							  "State",
+							  NM_ACTIVE_CONNECTION_STATE_UNKNOWN,
+							  NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+							  NM_ACTIVE_CONNECTION_STATE_UNKNOWN,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_DEFAULT,
+		 g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT,
+							   "Default",
+							   "Is the default IPv4 active connection",
+							   FALSE,
+							   G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_DEFAULT6,
+		 g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6,
+							   "Default6",
+							   "Is the default IPv6 active connection",
+							   FALSE,
+							   G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_VPN,
+		 g_param_spec_boolean (NM_ACTIVE_CONNECTION_VPN,
+							   "VPN",
+							   "Is a VPN connection",
+							   FALSE,
+							   G_PARAM_READABLE));
+
+	/* Signals */
+	signals[CONNECTION_SECRETS_UPDATED] =
+		g_signal_new ("connection-secrets-updated",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMActRequestClass, secrets_updated),
+					  NULL, NULL,
+					  _nm_marshal_VOID__OBJECT_POINTER_UINT,
+					  G_TYPE_NONE, 3,
+					  G_TYPE_OBJECT, G_TYPE_POINTER, G_TYPE_UINT);
+
+	signals[CONNECTION_SECRETS_FAILED] =
+		g_signal_new ("connection-secrets-failed",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMActRequestClass, secrets_failed),
+					  NULL, NULL,
+					  _nm_marshal_VOID__OBJECT_STRING_UINT,
+					  G_TYPE_NONE, 3,
+					  G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_UINT);
+
+	signals[PROPERTIES_CHANGED] = 
+		nm_properties_changed_signal_new (object_class,
+								    G_STRUCT_OFFSET (NMActRequestClass, properties_changed));
+
+	nm_active_connection_install_type_info (object_class);
+}
+
+static gboolean
+secrets_update_setting (NMSecretsProviderInterface *interface,
+                        const char *setting_name,
+                        GHashTable *new)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (interface);
+	NMSetting *setting = NULL;
+	GError *error = NULL;
+	GType type;
+
+	g_return_val_if_fail (priv->connection != NULL, FALSE);
+
+	/* Check whether a complete & valid NMSetting object was returned.  If
+	 * yes, replace the setting object in the connection.  If not, just try
+	 * updating the secrets.
+	 */
+	type = nm_connection_lookup_setting_type (setting_name);
+	if (type == 0)
+		return FALSE;
+
+	setting = nm_setting_new_from_hash (type, new);
+	if (setting) {
+		NMSetting *s_8021x = NULL;
+		GSList *all_settings = NULL;
+
+		/* The wireless-security setting might need the 802.1x setting in
+		 * the all_settings argument of the verify function. Ugh.
+		 */
+		s_8021x = nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_802_1X);
+		if (s_8021x)
+			all_settings = g_slist_append (all_settings, s_8021x);
+
+		if (!nm_setting_verify (setting, all_settings, NULL)) {
+			/* Just try updating secrets */
+			g_object_unref (setting);
+			setting = NULL;
+		}
+
+		g_slist_free (all_settings);
+	}
+
+	if (setting)
+		nm_connection_add_setting (priv->connection, setting);
+	else {
+		if (!nm_connection_update_secrets (priv->connection, setting_name, new, &error)) {
+			nm_log_warn (LOGD_DEVICE, "Failed to update connection secrets: %d %s",
+			             error ? error->code : -1,
+			             error && error->message ? error->message : "(none)");
+			g_clear_error (&error);
+		}
+	}
+
+	return TRUE;
+}
+
+static void
+secrets_result (NMSecretsProviderInterface *interface,
+	            const char *setting_name,
+	            RequestSecretsCaller caller,
+	            const GSList *updated,
+	            GError *error)
+{
+	NMActRequest *self = NM_ACT_REQUEST (interface);
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->connection != NULL);
+
+	if (error) {
+		g_signal_emit (self, signals[CONNECTION_SECRETS_FAILED], 0,
+		               priv->connection, setting_name, caller);
+	} else {
+		g_signal_emit (self, signals[CONNECTION_SECRETS_UPDATED], 0,
+		               priv->connection, updated, caller);
+	}
+}
+
+static void
+secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class)
+{
+	/* interface implementation */
+	sp_interface_class->update_setting = secrets_update_setting;
+	sp_interface_class->result = secrets_result;
+}
+
+gboolean
+nm_act_request_get_secrets (NMActRequest *self,
+                            const char *setting_name,
+                            gboolean request_new,
+                            RequestSecretsCaller caller,
+                            const char *hint1,
+                            const char *hint2)
+{
+	g_return_val_if_fail (self, FALSE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (self), FALSE);
+
+	return nm_secrets_provider_interface_get_secrets (NM_SECRETS_PROVIDER_INTERFACE (self),
+	                                                  nm_act_request_get_connection (self),
+	                                                  setting_name,
+	                                                  request_new,
+	                                                  caller,
+	                                                  hint1,
+	                                                  hint2);
+}
+
+NMConnection *
+nm_act_request_get_connection (NMActRequest *req)
+{
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NULL);
+
+	return NM_ACT_REQUEST_GET_PRIVATE (req)->connection;
+}
+
+const char *
+nm_act_request_get_specific_object (NMActRequest *req)
+{
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NULL);
+
+	return NM_ACT_REQUEST_GET_PRIVATE (req)->specific_object;
+}
+
+void
+nm_act_request_set_specific_object (NMActRequest *req,
+                                    const char *specific_object)
+{
+	NMActRequestPrivate *priv;
+
+	g_return_if_fail (NM_IS_ACT_REQUEST (req));
+	g_return_if_fail (specific_object != NULL);
+
+	priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+
+	if (priv->specific_object)
+		g_free (priv->specific_object);
+	priv->specific_object = g_strdup (specific_object);
+}
+
+gboolean
+nm_act_request_get_user_requested (NMActRequest *req)
+{
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	return NM_ACT_REQUEST_GET_PRIVATE (req)->user_requested;
+}
+
+const char *
+nm_act_request_get_active_connection_path (NMActRequest *req)
+{
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NULL);
+
+	return NM_ACT_REQUEST_GET_PRIVATE (req)->ac_path;
+}
+
+void
+nm_act_request_set_default (NMActRequest *req, gboolean is_default)
+{
+	NMActRequestPrivate *priv;
+
+	g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+	priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+	if (priv->is_default == is_default)
+		return;
+
+	priv->is_default = is_default;
+	g_object_notify (G_OBJECT (req), NM_ACTIVE_CONNECTION_DEFAULT);
+}
+
+gboolean
+nm_act_request_get_default (NMActRequest *req)
+{
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	return NM_ACT_REQUEST_GET_PRIVATE (req)->is_default;
+}
+
+void
+nm_act_request_set_default6 (NMActRequest *req, gboolean is_default6)
+{
+	NMActRequestPrivate *priv;
+
+	g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+	priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+	if (priv->is_default6 == is_default6)
+		return;
+
+	priv->is_default6 = is_default6;
+	g_object_notify (G_OBJECT (req), NM_ACTIVE_CONNECTION_DEFAULT6);
+}
+
+gboolean
+nm_act_request_get_default6 (NMActRequest *req)
+{
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	return NM_ACT_REQUEST_GET_PRIVATE (req)->is_default6;
+}
+
+static void
+share_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+void
+nm_act_request_set_shared (NMActRequest *req, gboolean shared)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+	GSList *list, *iter;
+
+	g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+	NM_ACT_REQUEST_GET_PRIVATE (req)->shared = shared;
+
+	/* Tear the rules down in reverse order when sharing is stopped */
+	list = g_slist_copy (priv->share_rules);
+	if (!shared)
+		list = g_slist_reverse (list);
+
+	/* Send the rules to iptables */
+	for (iter = list; iter; iter = g_slist_next (iter)) {
+		ShareRule *rule = (ShareRule *) iter->data;
+		char *envp[1] = { NULL };
+		char **argv;
+		char *cmd;
+
+		cmd = g_strdup_printf ("%s --table %s %s %s",
+		                       IPTABLES_PATH,
+		                       rule->table,
+		                       shared ? "--insert" : "--delete",
+		                       rule->rule);
+		if (!cmd)
+			continue;
+
+		argv = g_strsplit (cmd, " ", 0);
+		if (argv && argv[0]) {
+			int status;
+			GError *error = NULL;
+
+			nm_log_info (LOGD_SHARING, "Executing: %s", cmd);
+			if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+			                   share_child_setup, NULL, NULL, NULL, &status, &error)) {
+				nm_log_warn (LOGD_SHARING, "Error executing command: (%d) %s",
+				             error ? error->code : -1,
+				             (error && error->message) ? error->message : "(unknown)");
+				g_clear_error (&error);
+			} else if (WEXITSTATUS (status)) {
+				nm_log_warn (LOGD_SHARING, "** Command returned exit status %d.",
+				             WEXITSTATUS (status));
+			}
+		}
+		g_free (cmd);
+		if (argv)
+			g_strfreev (argv);
+	}
+
+	g_slist_free (list);
+
+	/* Clear the share rule list when sharing is stopped */
+	if (!shared)
+		clear_share_rules (req);
+}
+
+gboolean
+nm_act_request_get_shared (NMActRequest *req)
+{
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	return NM_ACT_REQUEST_GET_PRIVATE (req)->shared;
+}
+
+void
+nm_act_request_add_share_rule (NMActRequest *req,
+                               const char *table,
+                               const char *table_rule)
+{
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+	ShareRule *rule;
+
+	g_return_if_fail (NM_IS_ACT_REQUEST (req));
+	g_return_if_fail (table != NULL);
+	g_return_if_fail (table_rule != NULL);
+	
+	rule = g_malloc0 (sizeof (ShareRule));
+	rule->table = g_strdup (table);
+	rule->rule = g_strdup (table_rule);
+	priv->share_rules = g_slist_append (priv->share_rules, rule);
+}
+
+GObject *
+nm_act_request_get_device (NMActRequest *req)
+{
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	return G_OBJECT (NM_ACT_REQUEST_GET_PRIVATE (req)->device);
+}
+
+gboolean
+nm_act_request_get_assumed (NMActRequest *req)
+{
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	return NM_ACT_REQUEST_GET_PRIVATE (req)->assumed;
+}
+
diff --git a/src/nm-activation-request.h b/src/nm-activation-request.h
new file mode 100644
index 00000000..a2436945
--- /dev/null
+++ b/src/nm-activation-request.h
@@ -0,0 +1,103 @@
+/* -*- 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.
+ *
+ * (C) Copyright 2005 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_ACTIVATION_REQUEST_H
+#define NM_ACTIVATION_REQUEST_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "nm-connection.h"
+#include "nm-active-connection.h"
+#include "nm-secrets-provider-interface.h"
+
+#define NM_TYPE_ACT_REQUEST            (nm_act_request_get_type ())
+#define NM_ACT_REQUEST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACT_REQUEST, NMActRequest))
+#define NM_ACT_REQUEST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACT_REQUEST, NMActRequestClass))
+#define NM_IS_ACT_REQUEST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACT_REQUEST))
+#define NM_IS_ACT_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_ACT_REQUEST))
+#define NM_ACT_REQUEST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACT_REQUEST, NMActRequestClass))
+
+typedef struct {
+	GObject parent;
+} NMActRequest;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*secrets_updated)        (NMActRequest *req,
+	                                NMConnection *connection,
+	                                GSList *updated_settings,
+	                                RequestSecretsCaller caller);
+	void (*secrets_failed)         (NMActRequest *req,
+	                                NMConnection *connection,
+	                                const char *setting,
+	                                RequestSecretsCaller caller);
+
+	void (*properties_changed) (NMActRequest *req, GHashTable *properties);
+} NMActRequestClass;
+
+GType nm_act_request_get_type (void);
+
+NMActRequest *nm_act_request_new          (NMConnection *connection,
+                                           const char *specific_object,
+                                           gboolean user_requested,
+                                           gboolean assumed,
+                                           gpointer *device);  /* An NMDevice */
+
+NMConnection *nm_act_request_get_connection     (NMActRequest *req);
+const char *  nm_act_request_get_specific_object (NMActRequest *req);
+
+void          nm_act_request_set_specific_object (NMActRequest *req,
+                                                  const char *specific_object);
+
+gboolean      nm_act_request_get_user_requested (NMActRequest *req);
+
+const char *  nm_act_request_get_active_connection_path (NMActRequest *req);
+
+void          nm_act_request_set_default (NMActRequest *req, gboolean is_default);
+
+gboolean      nm_act_request_get_default (NMActRequest *req);
+
+void          nm_act_request_set_default6 (NMActRequest *req, gboolean is_default6);
+
+gboolean      nm_act_request_get_default6 (NMActRequest *req);
+
+gboolean      nm_act_request_get_shared (NMActRequest *req);
+
+void          nm_act_request_set_shared (NMActRequest *req, gboolean shared);
+
+void          nm_act_request_add_share_rule (NMActRequest *req,
+                                             const char *table,
+                                             const char *rule);
+
+GObject *     nm_act_request_get_device (NMActRequest *req);
+
+gboolean      nm_act_request_get_assumed (NMActRequest *req);
+
+gboolean nm_act_request_get_secrets    (NMActRequest *req,
+                                        const char *setting_name,
+                                        gboolean request_new,
+                                        RequestSecretsCaller caller,
+                                        const char *hint1,
+                                        const char *hint2);
+
+#endif /* NM_ACTIVATION_REQUEST_H */
+
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
new file mode 100644
index 00000000..4207e145
--- /dev/null
+++ b/src/nm-active-connection.c
@@ -0,0 +1,63 @@
+/* -*- 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) 2008 - 2010 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include "nm-active-connection.h"
+#include "NetworkManager.h"
+#include "nm-active-connection-glue.h"
+#include "nm-logging.h"
+
+char *
+nm_active_connection_get_next_object_path (void)
+{
+	static guint32 counter = 0;
+
+	return g_strdup_printf (NM_DBUS_PATH "/ActiveConnection/%d", counter++);
+}
+
+void
+nm_active_connection_install_type_info (GObjectClass *klass)
+{
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass),
+									 &dbus_glib_nm_active_connection_object_info);
+}
+
+void
+nm_active_connection_scope_to_value (NMConnection *connection, GValue *value)
+{
+	if (!connection) {
+		g_value_set_string (value, "");
+		return;
+	}
+
+	switch (nm_connection_get_scope (connection)) {
+	case NM_CONNECTION_SCOPE_SYSTEM:
+		g_value_set_string (value, NM_DBUS_SERVICE_SYSTEM_SETTINGS);
+		break;
+	case NM_CONNECTION_SCOPE_USER:
+		g_value_set_string (value, NM_DBUS_SERVICE_USER_SETTINGS);
+		break;
+	default:
+		nm_log_err (LOGD_CORE, "unknown connection scope!");
+		break;
+	}
+}
+
+
diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h
new file mode 100644
index 00000000..6a463cb2
--- /dev/null
+++ b/src/nm-active-connection.h
@@ -0,0 +1,42 @@
+/* -*- 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) 2008 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_ACTIVE_CONNECTION_H
+#define NM_ACTIVE_CONNECTION_H
+
+#include <glib-object.h>
+#include "nm-connection.h"
+
+#define NM_ACTIVE_CONNECTION_SERVICE_NAME "service-name"
+#define NM_ACTIVE_CONNECTION_CONNECTION "connection"
+#define NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT "specific-object"
+#define NM_ACTIVE_CONNECTION_DEVICES "devices"
+#define NM_ACTIVE_CONNECTION_STATE "state"
+#define NM_ACTIVE_CONNECTION_DEFAULT "default"
+#define NM_ACTIVE_CONNECTION_DEFAULT6 "default6"
+#define NM_ACTIVE_CONNECTION_VPN "vpn"
+
+char *nm_active_connection_get_next_object_path (void);
+
+void nm_active_connection_install_type_info (GObjectClass *klass);
+
+void nm_active_connection_scope_to_value (NMConnection *connection, GValue *value);
+
+#endif /* NM_ACTIVE_CONNECTION_H */
diff --git a/src/nm-call-store.c b/src/nm-call-store.c
new file mode 100644
index 00000000..df400d0f
--- /dev/null
+++ b/src/nm-call-store.c
@@ -0,0 +1,209 @@
+/* -*- 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) 2007 Novell, Inc.
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include "nm-call-store.h"
+#include "nm-logging.h"
+
+NMCallStore *
+nm_call_store_new (void)
+{
+	return g_hash_table_new_full (NULL, NULL, NULL,
+								  (GDestroyNotify) g_hash_table_destroy);
+}
+
+static void
+object_destroyed_cb (gpointer data, GObject *object)
+{
+	g_hash_table_remove ((NMCallStore *) data, object);
+}
+
+void
+nm_call_store_add (NMCallStore *store,
+				   GObject *object,
+				   gpointer *call_id)
+{
+	GHashTable *call_ids_hash;
+
+	g_return_if_fail (store != NULL);
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (call_id != NULL);
+
+	call_ids_hash = g_hash_table_lookup (store, object);
+	if (!call_ids_hash) {
+		call_ids_hash = g_hash_table_new (NULL, NULL);
+		g_hash_table_insert (store, object, call_ids_hash);
+		g_object_weak_ref (object, object_destroyed_cb, store);
+	}
+
+	g_hash_table_insert (call_ids_hash, call_id, NULL);
+}
+
+void
+nm_call_store_remove (NMCallStore *store,
+					  GObject *object,
+					  gpointer call_id)
+{
+	GHashTable *call_ids_hash;
+
+	g_return_if_fail (store != NULL);
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (call_id != NULL);
+
+	call_ids_hash = g_hash_table_lookup (store, object);
+	if (!call_ids_hash) {
+		nm_log_warn (LOGD_CORE, "Trying to remove a non-existant call id.");
+		return;
+	}
+
+	if (!g_hash_table_remove (call_ids_hash, call_id))
+		nm_log_warn (LOGD_CORE, "Trying to remove a non-existant call id.");
+
+	if (g_hash_table_size (call_ids_hash) == 0) {
+		g_hash_table_remove (store, object);
+		g_object_weak_unref (object, object_destroyed_cb, store);
+	}
+}
+
+typedef struct {
+	GObject *object;
+	gint count;
+	NMCallStoreFunc callback;
+	gpointer user_data;
+} StoreForeachInfo;
+
+static void
+call_callback (gpointer call_id, gpointer user_data)
+{
+	StoreForeachInfo *info = (StoreForeachInfo *) user_data;
+
+	if (info->count >= 0) {
+		if (info->callback (info->object, call_id, info->user_data))
+			info->count++;
+		else
+			info->count = -1;
+	}
+}
+
+static void
+prepend_id (gpointer key, gpointer value, gpointer user_data)
+{
+	GSList **list = (GSList **) user_data;
+
+	*list = g_slist_prepend (*list, key);
+}
+
+static GSList *
+get_call_ids (GHashTable *hash)
+{
+	GSList *ids = NULL;
+
+	g_hash_table_foreach (hash, prepend_id, &ids);
+
+	return ids;
+}
+
+static void
+call_all_callbacks (gpointer key, gpointer value, gpointer user_data)
+{
+	StoreForeachInfo *info = (StoreForeachInfo *) user_data;
+	GSList *ids;
+
+	info->object = G_OBJECT (key);
+
+	/* Create a copy of the hash keys (call_ids) so that the callback is
+	   free to modify the store */
+	ids = get_call_ids ((GHashTable *) value);
+	g_slist_foreach (ids, call_callback, info);
+	g_slist_free (ids);
+}
+
+static void
+duplicate_hash (gpointer key, gpointer value, gpointer user_data)
+{
+	g_hash_table_insert ((GHashTable *) user_data, key, value);
+}
+
+int
+nm_call_store_foreach (NMCallStore *store,
+					   GObject *object,
+					   NMCallStoreFunc callback,
+					   gpointer user_data)
+{
+	StoreForeachInfo info;
+
+	g_return_val_if_fail (store != NULL, -1);
+	g_return_val_if_fail (callback != NULL, -1);
+
+	info.object = object;
+	info.count = 0;
+	info.callback = callback;
+	info.user_data = user_data;
+
+	if (object) {
+		GHashTable *call_ids_hash;
+		GSList *ids;
+
+		call_ids_hash = g_hash_table_lookup (store, object);
+		if (!call_ids_hash) {
+			nm_log_warn (LOGD_CORE, "Object not in store");
+			return -1;
+		}
+
+		/* Create a copy of the hash keys (call_ids) so that the callback is
+		   free to modify the store */
+		ids = get_call_ids (call_ids_hash);
+		g_slist_foreach (ids, call_callback, &info);
+		g_slist_free (ids);
+	} else {
+		GHashTable *copy;
+
+		/* Create a copy of the main store so that callbacks can modify it */
+		copy = g_hash_table_new (NULL, NULL);
+		g_hash_table_foreach (store, duplicate_hash, copy);
+		g_hash_table_foreach (copy, call_all_callbacks, &info);
+		g_hash_table_destroy (copy);
+	}
+
+	return info.count;
+}
+
+static void
+remove_weakref (gpointer key, gpointer value, gpointer user_data)
+{
+	g_object_weak_unref (G_OBJECT (key), object_destroyed_cb, user_data);
+}
+
+void
+nm_call_store_clear (NMCallStore *store)
+{
+	g_return_if_fail (store);
+
+	g_hash_table_foreach (store, remove_weakref, store);
+	g_hash_table_remove_all (store);
+}
+
+void
+nm_call_store_destroy (NMCallStore *store)
+{
+	g_return_if_fail (store);
+
+	g_hash_table_destroy (store);
+}
diff --git a/src/nm-call-store.h b/src/nm-call-store.h
new file mode 100644
index 00000000..0ae5a769
--- /dev/null
+++ b/src/nm-call-store.h
@@ -0,0 +1,47 @@
+/* -*- 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.
+ *
+ * (C) Copyright 2007 Novell, Inc.
+ */
+
+#ifndef NM_CALLBACK_STORE_H
+#define NM_CALLBACK_STORE_H
+
+#include <glib-object.h>
+
+typedef GHashTable NMCallStore;
+
+typedef gboolean (*NMCallStoreFunc) (GObject *object, gpointer call_id, gpointer user_data);
+
+NMCallStore *nm_call_store_new     (void);
+void         nm_call_store_add     (NMCallStore *store,
+									GObject *object,
+									gpointer *call_id);
+
+void         nm_call_store_remove  (NMCallStore *store,
+									GObject *object,
+									gpointer call_id);
+
+int          nm_call_store_foreach (NMCallStore *store,
+									GObject *object,
+									NMCallStoreFunc callback,
+									gpointer user_data);
+
+void         nm_call_store_clear   (NMCallStore *store);
+void         nm_call_store_destroy (NMCallStore *store);
+
+#endif /* NM_CALLBACK_STORE_H */
diff --git a/src/nm-crash-logger.c b/src/nm-crash-logger.c
new file mode 100644
index 00000000..9ad0412d
--- /dev/null
+++ b/src/nm-crash-logger.c
@@ -0,0 +1,86 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ */
+
+
+#include <glib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+int main (int argc, char ** argv)
+{
+	GPid		gdb_pid;
+	int		out;
+	char		nm_pid[16];
+	char		line[256];
+	int		gdb_stat;
+	int		bytes_read;
+	gboolean	done = FALSE;
+	char *	args[] = { BINDIR "/gdb",
+                          "--batch", 
+                          "--quiet",
+                          "--command=" DATADIR "/NetworkManager/gdb-cmd",
+                          SBINDIR "/NetworkManager",
+                          NULL, NULL };
+
+	snprintf (nm_pid, sizeof (nm_pid), "%d", getppid ());
+	args[5] = &nm_pid[0];
+	if (!g_spawn_async_with_pipes (NULL, args, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
+				       &gdb_pid, NULL, &out, NULL, NULL))
+	{
+		exit (1);
+	}
+
+	openlog ("NetworkManager", LOG_CONS | LOG_PERROR, LOG_DAEMON);
+	syslog (LOG_CRIT, "******************* START **********************************");
+	while (!done)
+	{
+		bytes_read = read (out, line, sizeof (line) - 1);
+		if (bytes_read > 0)
+		{
+			char *end = &line[0];
+			char *start = &line[0];
+
+			/* Can't just funnel the output to syslog, have to do a separate
+			 * syslog () for each line in the output.
+			 */
+			line[bytes_read] = '\0';
+			while (*end != '\0')
+			{
+				if (*end == '\n')
+				{
+					*end = '\0';
+					syslog (LOG_CRIT, "%s", start);
+					start = end + 1;
+				}
+				end++;
+			}
+		}
+		else if ((bytes_read <= 0) || ((errno != EINTR) && (errno != EAGAIN)))
+			done = TRUE;
+	}
+	syslog (LOG_CRIT, "******************* END **********************************");
+	close (out);
+	waitpid (gdb_pid, &gdb_stat, 0);
+	exit (0);
+}
diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c
new file mode 100644
index 00000000..9b621b42
--- /dev/null
+++ b/src/nm-dbus-manager.c
@@ -0,0 +1,378 @@
+/* -*- 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) 2006 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include "config.h"
+#include "NetworkManager.h"
+#include "nm-dbus-manager.h"
+#include "nm-marshal.h"
+#include "nm-glib-compat.h"
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <string.h>
+#include "nm-logging.h"
+
+enum {
+	DBUS_CONNECTION_CHANGED = 0,
+	NAME_OWNER_CHANGED,
+	NUMBER_OF_SIGNALS
+};
+
+static guint signals[NUMBER_OF_SIGNALS];
+
+G_DEFINE_TYPE(NMDBusManager, nm_dbus_manager, G_TYPE_OBJECT)
+
+#define NM_DBUS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                        NM_TYPE_DBUS_MANAGER, \
+                                        NMDBusManagerPrivate))
+
+typedef struct {
+	DBusConnection *connection;
+	DBusGConnection *g_connection;
+	gboolean started;
+
+	DBusGProxy *proxy;
+	guint proxy_destroy_id;
+
+	guint reconnect_id;
+} NMDBusManagerPrivate;
+
+static gboolean nm_dbus_manager_init_bus (NMDBusManager *self);
+static void nm_dbus_manager_cleanup (NMDBusManager *self, gboolean dispose);
+static void start_reconnection_timeout (NMDBusManager *self);
+
+NMDBusManager *
+nm_dbus_manager_get (void)
+{
+	static NMDBusManager *singleton = NULL;
+
+	if (!singleton) {
+		singleton = NM_DBUS_MANAGER (g_object_new (NM_TYPE_DBUS_MANAGER, NULL));
+		if (!nm_dbus_manager_init_bus (singleton))
+			start_reconnection_timeout (singleton);
+	} else {
+		g_object_ref (singleton);
+	}
+
+	g_assert (singleton);
+	return singleton;
+}
+
+static void
+nm_dbus_manager_init (NMDBusManager *self)
+{
+}
+
+static void
+nm_dbus_manager_dispose (GObject *object)
+{
+	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (object);
+
+	nm_dbus_manager_cleanup (NM_DBUS_MANAGER (object), TRUE);
+
+	if (priv->reconnect_id) {
+		g_source_remove (priv->reconnect_id);
+		priv->reconnect_id = 0;
+	}
+
+	G_OBJECT_CLASS (nm_dbus_manager_parent_class)->dispose (object);
+}
+
+static void
+nm_dbus_manager_class_init (NMDBusManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = nm_dbus_manager_dispose;
+
+	signals[DBUS_CONNECTION_CHANGED] =
+		g_signal_new ("dbus-connection-changed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMDBusManagerClass, dbus_connection_changed),
+		              NULL, NULL, _nm_marshal_VOID__POINTER,
+		              G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	signals[NAME_OWNER_CHANGED] =
+		g_signal_new ("name-owner-changed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMDBusManagerClass, name_owner_changed),
+		              NULL, NULL, _nm_marshal_VOID__STRING_STRING_STRING,
+		              G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+
+	g_type_class_add_private (klass, sizeof (NMDBusManagerPrivate));
+}
+
+
+/* Only cleanup a specific dbus connection, not all our private data */
+static void
+nm_dbus_manager_cleanup (NMDBusManager *self, gboolean dispose)
+{
+	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
+
+	if (priv->proxy) {
+		if (dispose) {
+			g_signal_handler_disconnect (priv->proxy, priv->proxy_destroy_id);
+			priv->proxy_destroy_id = 0;
+		}
+		g_object_unref (priv->proxy);
+		priv->proxy = NULL;
+	}
+
+	if (priv->g_connection) {
+		dbus_g_connection_unref (priv->g_connection);
+		priv->g_connection = NULL;
+		priv->connection = NULL;
+	}
+
+	priv->started = FALSE;
+}
+
+static gboolean
+nm_dbus_manager_reconnect (gpointer user_data)
+{
+	NMDBusManager *self = NM_DBUS_MANAGER (user_data);
+	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
+
+	g_assert (self != NULL);
+
+	if (nm_dbus_manager_init_bus (self)) {
+		if (nm_dbus_manager_start_service (self)) {
+			nm_log_info (LOGD_CORE, "reconnected to the system bus.");
+			g_signal_emit (self, signals[DBUS_CONNECTION_CHANGED],
+			               0, priv->connection);
+			priv->reconnect_id = 0;
+			return FALSE;
+		}
+	}
+
+	/* Try again */
+	nm_dbus_manager_cleanup (self, FALSE);
+	return TRUE;
+}
+
+static void
+start_reconnection_timeout (NMDBusManager *self)
+{
+	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
+
+	if (priv->reconnect_id)
+		g_source_remove (priv->reconnect_id);
+
+	/* Schedule timeout for reconnection attempts */
+	priv->reconnect_id = g_timeout_add_seconds (3, nm_dbus_manager_reconnect, self);
+}
+
+char *
+nm_dbus_manager_get_name_owner (NMDBusManager *self,
+                                const char *name,
+                                GError **error)
+{
+	char *owner = NULL;
+
+	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL);
+	g_return_val_if_fail (name != NULL, NULL);
+	if (error)
+		g_return_val_if_fail (*error == NULL, NULL);
+
+	if (!dbus_g_proxy_call_with_timeout (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy,
+	                                     "GetNameOwner", 2000, error,
+	                                     G_TYPE_STRING, name,
+	                                     G_TYPE_INVALID,
+	                                     G_TYPE_STRING, &owner,
+	                                     G_TYPE_INVALID)) {
+		return NULL;
+	}
+
+	return owner;
+}
+
+gboolean
+nm_dbus_manager_name_has_owner (NMDBusManager *self,
+                                const char *name)
+{
+	gboolean has_owner = FALSE;
+	GError *err = NULL;
+
+	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE);
+	g_return_val_if_fail (name != NULL, FALSE);
+
+	if (!dbus_g_proxy_call (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy,
+					    "NameHasOwner", &err,
+					    G_TYPE_STRING, name,
+					    G_TYPE_INVALID,
+					    G_TYPE_BOOLEAN, &has_owner,
+					    G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_CORE, "NameHasOwner request failed: %s",
+		             (err && err->message) ? err->message : "(unknown)");
+		g_clear_error (&err);
+	}
+
+	return has_owner;
+}
+
+static void
+proxy_name_owner_changed (DBusGProxy *proxy,
+					 const char *name,
+					 const char *old_owner,
+					 const char *new_owner,
+					 gpointer user_data)
+{
+	g_signal_emit (G_OBJECT (user_data), signals[NAME_OWNER_CHANGED],
+	               0, name, old_owner, new_owner);
+}
+
+static void
+destroy_cb (DBusGProxy *proxy, gpointer user_data)
+{
+	NMDBusManager *self = NM_DBUS_MANAGER (user_data);
+
+	/* Clean up existing connection */
+	nm_log_warn (LOGD_CORE, "disconnected by the system bus.");
+	NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy = NULL;
+
+	nm_dbus_manager_cleanup (self, FALSE);
+
+	g_signal_emit (G_OBJECT (self), signals[DBUS_CONNECTION_CHANGED], 0, NULL);
+
+	start_reconnection_timeout (self);
+}
+
+static gboolean
+nm_dbus_manager_init_bus (NMDBusManager *self)
+{
+	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
+	GError *err = NULL;
+
+	if (priv->connection) {
+		nm_log_warn (LOGD_CORE, "DBus Manager already has a valid connection.");
+		return FALSE;
+	}
+
+	dbus_connection_set_change_sigpipe (TRUE);
+
+	priv->g_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
+	if (!priv->g_connection) {
+		nm_log_err (LOGD_CORE, "Could not get the system bus.  Make sure "
+		            "the message bus daemon is running!  Message: %s",
+		            err->message);
+		g_error_free (err);
+		return FALSE;
+	}
+
+	priv->connection = dbus_g_connection_get_connection (priv->g_connection);
+	dbus_connection_set_exit_on_disconnect (priv->connection, FALSE);
+
+	priv->proxy = dbus_g_proxy_new_for_name (priv->g_connection,
+	                                         "org.freedesktop.DBus",
+	                                         "/org/freedesktop/DBus",
+	                                         "org.freedesktop.DBus");
+
+	priv->proxy_destroy_id = g_signal_connect (priv->proxy, "destroy",
+	                                           G_CALLBACK (destroy_cb), self);
+
+	dbus_g_proxy_add_signal (priv->proxy, "NameOwnerChanged",
+	                         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+	                         G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy,
+	                             "NameOwnerChanged",
+	                             G_CALLBACK (proxy_name_owner_changed),
+	                             self, NULL);
+	return TRUE;
+}
+
+/* Register our service on the bus; shouldn't be called until
+ * all necessary message handlers have been registered, because
+ * when we register on the bus, clients may start to call.
+ */
+gboolean
+nm_dbus_manager_start_service (NMDBusManager *self)
+{
+	NMDBusManagerPrivate *priv;
+	int result;
+	GError *err = NULL;
+
+	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE);
+
+	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
+
+	if (priv->started) {
+		nm_log_err (LOGD_CORE, "Service has already started.");
+		return FALSE;
+	}
+
+	if (!dbus_g_proxy_call (priv->proxy, "RequestName", &err,
+	                        G_TYPE_STRING, NM_DBUS_SERVICE,
+	                        G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
+	                        G_TYPE_INVALID,
+	                        G_TYPE_UINT, &result,
+	                        G_TYPE_INVALID)) {
+		nm_log_err (LOGD_CORE, "Could not acquire the NetworkManager service.\n"
+		            "  Error: '%s'",
+		            (err && err->message) ? err->message : "(unknown)");
+		g_error_free (err);
+		return FALSE;
+	}
+
+	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		nm_log_err (LOGD_CORE, "Could not acquire the NetworkManager service as it is already taken.");
+		return FALSE;
+	}
+
+	if (!dbus_g_proxy_call (priv->proxy, "RequestName", &err,
+							G_TYPE_STRING, NM_DBUS_SERVICE_SYSTEM_SETTINGS,
+							G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
+							G_TYPE_INVALID,
+							G_TYPE_UINT, &result,
+							G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_CORE, "Could not acquire the NetworkManagerSystemSettings service.\n"
+		             "  Message: '%s'", err->message);
+		g_error_free (err);
+		return FALSE;
+	}
+
+	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		nm_log_warn (LOGD_CORE, "Could not acquire the NetworkManagerSystemSettings service "
+		             "as it is already taken.");
+		return FALSE;
+	}
+
+	priv->started = TRUE;
+	return priv->started;
+}
+
+DBusConnection *
+nm_dbus_manager_get_dbus_connection (NMDBusManager *self)
+{
+	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL);
+
+	return NM_DBUS_MANAGER_GET_PRIVATE (self)->connection;
+}
+
+DBusGConnection *
+nm_dbus_manager_get_connection (NMDBusManager *self)
+{
+	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL);
+
+	return NM_DBUS_MANAGER_GET_PRIVATE (self)->g_connection;
+}
diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h
new file mode 100644
index 00000000..275ae4e0
--- /dev/null
+++ b/src/nm-dbus-manager.h
@@ -0,0 +1,78 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef __NM_DBUS_MANAGER_H__
+#define __NM_DBUS_MANAGER_H__
+
+#include "config.h"
+#include <glib-object.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+typedef gboolean (* NMDBusSignalHandlerFunc) (DBusConnection * connection,
+                                              DBusMessage *    message,
+                                              gpointer         user_data);
+
+#define NM_TYPE_DBUS_MANAGER (nm_dbus_manager_get_type ())
+#define NM_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DBUS_MANAGER, NMDBusManager))
+#define NM_DBUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass))
+#define NM_IS_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_DBUS_MANAGER))
+#define NM_IS_DBUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_DBUS_MANAGER))
+#define NM_DBUS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass)) 
+
+typedef struct {
+	GObject parent;
+} NMDBusManager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*dbus_connection_changed) (NMDBusManager *mgr,
+	                                 DBusConnection *connection);
+
+	void (*name_owner_changed)      (NMDBusManager *mgr,
+	                                 const char *name,
+	                                 const char *old_owner,
+	                                 const char *new_owner);
+} NMDBusManagerClass;
+
+GType nm_dbus_manager_get_type (void);
+
+NMDBusManager * nm_dbus_manager_get       (void);
+
+char * nm_dbus_manager_get_name_owner     (NMDBusManager *self,
+                                           const char *name,
+                                           GError **error);
+
+gboolean nm_dbus_manager_start_service    (NMDBusManager *self);
+
+gboolean nm_dbus_manager_name_has_owner   (NMDBusManager *self,
+                                           const char *name);
+
+DBusConnection * nm_dbus_manager_get_dbus_connection (NMDBusManager *self);
+DBusGConnection * nm_dbus_manager_get_connection (NMDBusManager *self);
+
+G_END_DECLS
+
+#endif /* __NM_DBUS_MANAGER_H__ */
diff --git a/src/nm-device-bt.c b/src/nm-device-bt.c
new file mode 100644
index 00000000..3ef08de8
--- /dev/null
+++ b/src/nm-device-bt.c
@@ -0,0 +1,1073 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <net/ethernet.h>
+
+#include "nm-glib-compat.h"
+#include "nm-bluez-common.h"
+#include "nm-dbus-manager.h"
+#include "nm-device-bt.h"
+#include "nm-device-interface.h"
+#include "nm-device-private.h"
+#include "nm-logging.h"
+#include "nm-marshal.h"
+#include "ppp-manager/nm-ppp-manager.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-bluetooth.h"
+#include "nm-setting-cdma.h"
+#include "nm-setting-gsm.h"
+#include "nm-device-bt-glue.h"
+#include "NetworkManagerUtils.h"
+
+#define BLUETOOTH_DUN_UUID "dun"
+#define BLUETOOTH_NAP_UUID "nap"
+
+G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_BT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BT, NMDeviceBtPrivate))
+
+typedef struct {
+	char *bdaddr;
+	char *name;
+	guint32 capabilities;
+
+	gboolean connected;
+	gboolean have_iface;
+
+	DBusGProxy *type_proxy;
+	DBusGProxy *dev_proxy;
+
+	char *rfcomm_iface;
+	NMModem *modem;
+	guint32 timeout_id;
+
+	guint32 bt_type;  /* BT type of the current connection */
+} NMDeviceBtPrivate;
+
+enum {
+	PROP_0,
+	PROP_HW_ADDRESS,
+	PROP_BT_NAME,
+	PROP_BT_CAPABILITIES,
+
+	LAST_PROP
+};
+
+enum {
+	PPP_STATS,
+	PROPERTIES_CHANGED,
+
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+typedef enum {
+	NM_BT_ERROR_CONNECTION_NOT_BT = 0,
+	NM_BT_ERROR_CONNECTION_INVALID,
+	NM_BT_ERROR_CONNECTION_INCOMPATIBLE,
+} NMBtError;
+
+#define NM_BT_ERROR (nm_bt_error_quark ())
+#define NM_TYPE_BT_ERROR (nm_bt_error_get_type ())
+
+static GQuark
+nm_bt_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-bt-error");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+static GType
+nm_bt_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Connection was not a BT connection. */
+			ENUM_ENTRY (NM_BT_ERROR_CONNECTION_NOT_BT, "ConnectionNotBt"),
+			/* Connection was not a valid BT connection. */
+			ENUM_ENTRY (NM_BT_ERROR_CONNECTION_INVALID, "ConnectionInvalid"),
+			/* Connection does not apply to this device. */
+			ENUM_ENTRY (NM_BT_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMBtError", values);
+	}
+	return etype;
+}
+
+
+guint32 nm_device_bt_get_capabilities (NMDeviceBt *self)
+{
+	g_return_val_if_fail (self != NULL, NM_BT_CAPABILITY_NONE);
+	g_return_val_if_fail (NM_IS_DEVICE_BT (self), NM_BT_CAPABILITY_NONE);
+
+	return NM_DEVICE_BT_GET_PRIVATE (self)->capabilities;
+}
+
+const char *nm_device_bt_get_hw_address (NMDeviceBt *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE_BT (self), NULL);
+
+	return NM_DEVICE_BT_GET_PRIVATE (self)->bdaddr;
+}
+
+static guint32
+get_connection_bt_type (NMConnection *connection)
+{
+	NMSettingBluetooth *s_bt;
+	const char *bt_type;
+
+	s_bt = (NMSettingBluetooth *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH);
+	if (!s_bt)
+		return NM_BT_CAPABILITY_NONE;
+
+	bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
+	g_assert (bt_type);
+
+	if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
+		return NM_BT_CAPABILITY_DUN;
+	else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
+		return NM_BT_CAPABILITY_NAP;
+
+	return NM_BT_CAPABILITY_NONE;
+}
+
+static NMConnection *
+real_get_best_auto_connection (NMDevice *device,
+                               GSList *connections,
+                               char **specific_object)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	GSList *iter;
+
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		NMConnection *connection = NM_CONNECTION (iter->data);
+		NMSettingConnection *s_con;
+		guint32 bt_type;
+
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+		g_assert (s_con);
+
+		if (!nm_setting_connection_get_autoconnect (s_con))
+			continue;
+
+		if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_BLUETOOTH_SETTING_NAME))
+			continue;
+
+		bt_type = get_connection_bt_type (connection);
+		if (!(bt_type & priv->capabilities))
+			continue;
+
+		return connection;
+	}
+	return NULL;
+}
+
+static gboolean
+real_check_connection_compatible (NMDevice *device,
+                                  NMConnection *connection,
+                                  GError **error)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMSettingConnection *s_con;
+	NMSettingBluetooth *s_bt;
+	const GByteArray *array;
+	char *str;
+	int addr_match = FALSE;
+	guint32 bt_type;
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_BLUETOOTH_SETTING_NAME)) {
+		g_set_error (error,
+		             NM_BT_ERROR, NM_BT_ERROR_CONNECTION_NOT_BT,
+		             "The connection was not a Bluetooth connection.");
+		return FALSE;
+	}
+
+	s_bt = NM_SETTING_BLUETOOTH (nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH));
+	if (!s_bt) {
+		g_set_error (error,
+		             NM_BT_ERROR, NM_BT_ERROR_CONNECTION_INVALID,
+		             "The connection was not a valid Bluetooth connection.");
+		return FALSE;
+	}
+
+	array = nm_setting_bluetooth_get_bdaddr (s_bt);
+	if (!array || (array->len != ETH_ALEN)) {
+		g_set_error (error,
+		             NM_BT_ERROR, NM_BT_ERROR_CONNECTION_INVALID,
+		             "The connection did not contain a valid Bluetooth address.");
+		return FALSE;
+	}
+
+	bt_type = get_connection_bt_type (connection);
+	if (!(bt_type & priv->capabilities)) {
+		g_set_error (error,
+		             NM_BT_ERROR, NM_BT_ERROR_CONNECTION_INCOMPATIBLE,
+		             "The connection was not compatible with the device's capabilities.");
+		return FALSE;
+	}
+
+	str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+	                       array->data[0], array->data[1], array->data[2],
+	                       array->data[3], array->data[4], array->data[5]);
+	addr_match = !strcmp (priv->bdaddr, str);
+	g_free (str);
+
+	return addr_match;
+}
+
+static guint32
+real_get_generic_capabilities (NMDevice *dev)
+{
+	return NM_DEVICE_CAP_NM_SUPPORTED;
+}
+
+/*****************************************************************************/
+/* IP method PPP */
+
+static void
+ppp_stats (NMModem *modem,
+		   guint32 in_bytes,
+		   guint32 out_bytes,
+		   gpointer user_data)
+{
+	g_signal_emit (NM_DEVICE_BT (user_data), signals[PPP_STATS], 0, in_bytes, out_bytes);
+}
+
+static void
+ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+
+	switch (nm_device_interface_get_state (NM_DEVICE_INTERFACE (device))) {
+	case NM_DEVICE_STATE_PREPARE:
+	case NM_DEVICE_STATE_CONFIG:
+	case NM_DEVICE_STATE_NEED_AUTH:
+	case NM_DEVICE_STATE_ACTIVATED:
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		break;
+	case NM_DEVICE_STATE_IP_CONFIG:
+		if (nm_device_ip_config_should_fail (device, FALSE)) {
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+modem_need_auth (NMModem *modem,
+	             const char *setting_name,
+	             gboolean retry,
+	             RequestSecretsCaller caller,
+	             const char *hint1,
+	             const char *hint2,
+	             gpointer user_data)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMActRequest *req;
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	g_assert (req);
+
+	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
+	nm_act_request_get_secrets (req, setting_name, retry, caller, hint1, hint2);
+}
+
+static void
+modem_prepare_result (NMModem *modem,
+                      gboolean success,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceState state;
+
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device));
+	g_return_if_fail (state == NM_DEVICE_STATE_CONFIG || state == NM_DEVICE_STATE_NEED_AUTH);
+
+	if (success) {
+		NMActRequest *req;
+		NMActStageReturn ret;
+		NMDeviceStateReason stage2_reason = NM_DEVICE_STATE_REASON_NONE;
+
+		req = nm_device_get_act_request (device);
+		g_assert (req);
+
+		ret = nm_modem_act_stage2_config (modem, req, &stage2_reason);
+		switch (ret) {
+		case NM_ACT_STAGE_RETURN_POSTPONE:
+			break;
+		case NM_ACT_STAGE_RETURN_SUCCESS:
+			nm_device_activate_schedule_stage3_ip_config_start (device);
+			break;
+		case NM_ACT_STAGE_RETURN_FAILURE:
+		default:
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, stage2_reason);
+			break;
+		}
+	} else
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+}
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+
+	if (priv->modem)
+		nm_modem_device_state_changed (priv->modem, new_state, old_state, reason);
+}
+
+static void
+modem_ip4_config_result (NMModem *self,
+                         const char *iface,
+                         NMIP4Config *config,
+                         GError *error,
+                         gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceState state;
+
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device));
+	g_return_if_fail (state == NM_DEVICE_STATE_IP_CONFIG);
+
+	if (error) {
+		nm_log_warn (LOGD_MB | LOGD_IP4 | LOGD_BT,
+		             "(%s): retrieving IP4 configuration failed: (%d) %s",
+		             nm_device_get_ip_iface (device),
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+	} else {
+		if (iface)
+			nm_device_set_ip_iface (device, iface);
+
+		nm_device_activate_schedule_stage4_ip4_config_get (device);
+	}
+}
+
+static gboolean
+modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *reason)
+{
+	NMActRequest *req;
+	NMActStageReturn ret;
+
+	g_return_val_if_fail (reason != NULL, FALSE);
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	g_assert (req);
+
+	ret = nm_modem_act_stage1_prepare (modem, req, reason);
+	switch (ret) {
+	case NM_ACT_STAGE_RETURN_POSTPONE:
+	case NM_ACT_STAGE_RETURN_SUCCESS:
+		/* Success, wait for the 'prepare-result' signal */
+		return TRUE;
+	case NM_ACT_STAGE_RETURN_FAILURE:
+	default:
+		break;
+	}
+
+	return FALSE;
+}
+
+static void
+real_connection_secrets_updated (NMDevice *device,
+                                 NMConnection *connection,
+                                 GSList *updated_settings,
+                                 RequestSecretsCaller caller)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (device);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	NMActRequest *req;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (device)));
+
+	req = nm_device_get_act_request (device);
+	g_assert (req);
+
+	if (!nm_modem_connection_secrets_updated (priv->modem,
+                                              req,
+                                              connection,
+                                              updated_settings,
+                                              caller)) {
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS);
+		return;
+	}
+
+	/* PPP handles stuff itself... */
+	if (caller == SECRETS_CALLER_PPP)
+		return;
+
+	/* Otherwise, on success for GSM/CDMA secrets we need to schedule modem stage1 again */
+	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
+	if (!modem_stage1 (self, priv->modem, &reason))
+		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason);
+}
+
+/*****************************************************************************/
+
+gboolean
+nm_device_bt_modem_added (NMDeviceBt *self,
+                          NMModem *modem,
+                          const char *driver)
+{
+	NMDeviceBtPrivate *priv;
+	const char *modem_iface;
+	char *base;
+	NMDeviceState state;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE_BT (self), FALSE);
+	g_return_val_if_fail (modem != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_MODEM (modem), FALSE);
+
+	priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	modem_iface = nm_modem_get_iface (modem);
+	g_return_val_if_fail (modem_iface != NULL, FALSE);
+
+	if (!priv->rfcomm_iface)
+		return FALSE;
+
+	base = g_path_get_basename (priv->rfcomm_iface);
+	if (strcmp (base, modem_iface)) {
+		g_free (base);
+		return FALSE;
+	}
+	g_free (base);
+
+	/* Got the modem */
+	if (priv->timeout_id) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	/* Can only accept the modem in stage2, but since the interface matched
+	 * what we were expecting, don't let anything else claim the modem either.
+	 */
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
+	if (state != NM_DEVICE_STATE_CONFIG) {
+		nm_log_warn (LOGD_BT | LOGD_MB,
+		             "(%s): modem found but device not in correct state (%d)",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             nm_device_get_state (NM_DEVICE (self)));
+		return TRUE;
+	}
+
+	nm_log_info (LOGD_BT | LOGD_MB,
+	             "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) modem found.",
+	             nm_device_get_iface (NM_DEVICE (self)));
+
+	if (priv->modem) {
+		g_warn_if_reached ();
+		g_object_unref (priv->modem);
+	}
+
+	priv->modem = g_object_ref (modem);
+	g_signal_connect (modem, NM_MODEM_PPP_STATS, G_CALLBACK (ppp_stats), self);
+	g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
+	g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
+	g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self);
+	g_signal_connect (modem, NM_MODEM_NEED_AUTH, G_CALLBACK (modem_need_auth), self);
+
+	/* Kick off the modem connection */
+	if (!modem_stage1 (self, modem, &reason))
+		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason);
+
+	return TRUE;
+}
+
+gboolean
+nm_device_bt_modem_removed (NMDeviceBt *self, NMModem *modem)
+{
+	NMDeviceBtPrivate *priv;
+	NMDeviceState state;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE_BT (self), FALSE);
+	g_return_val_if_fail (modem != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_MODEM (modem), FALSE);
+
+	priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+	if (modem != priv->modem)
+		return FALSE;
+
+	state = nm_device_get_state (NM_DEVICE (self));
+	nm_modem_device_state_changed (priv->modem,
+	                               NM_DEVICE_STATE_DISCONNECTED,
+	                               state,
+	                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
+
+	g_object_unref (priv->modem);
+	priv->modem = NULL;
+	return TRUE;
+}
+
+static gboolean
+modem_find_timeout (gpointer user_data)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+
+	NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0;
+	nm_device_state_changed (NM_DEVICE (self),
+	                         NM_DEVICE_STATE_FAILED,
+	                         NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND);
+	return FALSE;
+}
+
+static void
+check_connect_continue (NMDeviceBt *self)
+{
+	NMDevice *device = NM_DEVICE (self);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gboolean pan = (priv->bt_type == NM_BT_CAPABILITY_NAP);
+	gboolean dun = (priv->bt_type == NM_BT_CAPABILITY_DUN);
+
+	if (!priv->connected || !priv->have_iface)
+		return;
+
+	nm_log_info (LOGD_BT, "Activation (%s %s/bluetooth) Stage 2 of 5 (Device Configure) "
+	             "successful.  Will connect via %s.",
+	             nm_device_get_iface (device),
+	             nm_device_get_ip_iface (device),
+	             dun ? "DUN" : (pan ? "PAN" : "unknown"));
+
+	/* Kill the connect timeout since we're connected now */
+	if (priv->timeout_id) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	if (pan) {
+		/* Bluez says we're connected now.  Start IP config. */
+		nm_device_activate_schedule_stage3_ip_config_start (device);
+	} else if (dun) {
+		/* Wait for ModemManager to find the modem */
+		priv->timeout_id = g_timeout_add_seconds (30, modem_find_timeout, self);
+
+		nm_log_info (LOGD_BT | LOGD_MB, "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) "
+		             "waiting for modem to appear.",
+		             nm_device_get_iface (device));
+	} else
+		g_assert_not_reached ();
+}
+
+static void
+bluez_connect_cb (DBusGProxy *proxy,
+                  DBusGProxyCall *call_id,
+                  void *user_data)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	GError *error = NULL;
+	char *device;
+
+	if (dbus_g_proxy_end_call (proxy, call_id, &error,
+	                           G_TYPE_STRING, &device,
+	                           G_TYPE_INVALID) == FALSE) {
+		nm_log_warn (LOGD_BT, "Error connecting with bluez: %s",
+		             error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_BT_FAILED);
+		return;
+	}
+
+	if (!device || !strlen (device)) {
+		nm_log_warn (LOGD_BT, "Invalid network device returned by bluez");
+
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_BT_FAILED);
+	}
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+		g_free (priv->rfcomm_iface);
+		priv->rfcomm_iface = device;
+	} else if (priv->bt_type == NM_BT_CAPABILITY_NAP) {
+		nm_device_set_ip_iface (NM_DEVICE (self), device);
+		g_free (device);
+	}
+
+	nm_log_dbg (LOGD_BT, "(%s): connect request successful",
+	            nm_device_get_iface (NM_DEVICE (self)));
+
+	/* Stage 3 gets scheduled when Bluez says we're connected */
+	priv->have_iface = TRUE;
+	check_connect_continue (self);
+}
+
+static void
+bluez_property_changed (DBusGProxy *proxy,
+                        const char *property,
+                        GValue *value,
+                        gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gboolean connected;
+	NMDeviceState state;
+	const char *prop_str = "(unknown)";
+
+	if (G_VALUE_HOLDS_STRING (value))
+		prop_str = g_value_get_string (value);
+	else if (G_VALUE_HOLDS_BOOLEAN (value))
+		prop_str = g_value_get_boolean (value) ? "true" : "false";
+
+	nm_log_dbg (LOGD_BT, "(%s): bluez property '%s' changed to '%s'",
+	            nm_device_get_iface (device),
+	            property,
+	            prop_str);
+
+	if (strcmp (property, "Connected"))
+		return;
+
+	state = nm_device_get_state (device);
+	connected = g_value_get_boolean (value);
+	if (connected) {
+		if (state == NM_DEVICE_STATE_CONFIG) {
+			nm_log_dbg (LOGD_BT, "(%s): connected to the device",
+			            nm_device_get_iface (device));
+
+			priv->connected = TRUE;
+			check_connect_continue (self);
+		}
+	} else {
+		gboolean fail = FALSE;
+
+		/* Bluez says we're disconnected from the device.  Suck. */
+
+		if (nm_device_is_activating (device)) {
+			nm_log_info (LOGD_BT,
+			             "Activation (%s/bluetooth): bluetooth link disconnected.",
+			             nm_device_get_iface (device));
+			fail = TRUE;
+		} else if (state == NM_DEVICE_STATE_ACTIVATED) {
+			nm_log_info (LOGD_BT, "(%s): bluetooth link disconnected.",
+			             nm_device_get_iface (device));
+			fail = TRUE;
+		}
+
+		if (fail) {
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CARRIER);
+			priv->connected = FALSE;
+		}
+	}
+}
+
+static gboolean
+bt_connect_timeout (gpointer user_data)
+{
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+
+	nm_log_dbg (LOGD_BT, "(%s): initial connection timed out",
+	            nm_device_get_iface (NM_DEVICE (self)));
+
+	NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0;
+	nm_device_state_changed (NM_DEVICE (self),
+	                         NM_DEVICE_STATE_FAILED,
+	                         NM_DEVICE_STATE_REASON_BT_FAILED);
+	return FALSE;
+}
+
+static NMActStageReturn
+real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMActRequest *req;
+	NMDBusManager *dbus_mgr;
+	DBusGConnection *g_connection;
+	gboolean dun = FALSE;
+
+	req = nm_device_get_act_request (device);
+	g_assert (req);
+
+	priv->bt_type = get_connection_bt_type (nm_act_request_get_connection (req));
+	if (priv->bt_type == NM_BT_CAPABILITY_NONE) {
+		// FIXME: set a reason code
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	dbus_mgr = nm_dbus_manager_get ();
+	g_connection = nm_dbus_manager_get_connection (dbus_mgr);
+	g_object_unref (dbus_mgr);
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN)
+		dun = TRUE;
+	else if (priv->bt_type == NM_BT_CAPABILITY_NAP)
+		dun = FALSE;
+	else
+		g_assert_not_reached ();
+
+	priv->dev_proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                               BLUEZ_SERVICE,
+	                                               nm_device_get_udi (device),
+	                                               BLUEZ_DEVICE_INTERFACE);
+	if (!priv->dev_proxy) {
+		// FIXME: set a reason code
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	/* Watch for BT device property changes */
+	dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED,
+	                                   G_TYPE_NONE,
+	                                   G_TYPE_STRING, G_TYPE_VALUE,
+	                                   G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->dev_proxy, "PropertyChanged",
+	                         G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->dev_proxy, "PropertyChanged",
+	                             G_CALLBACK (bluez_property_changed), device, NULL);
+
+	priv->type_proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                              BLUEZ_SERVICE,
+	                                              nm_device_get_udi (device),
+	                                              dun ? BLUEZ_SERIAL_INTERFACE : BLUEZ_NETWORK_INTERFACE);
+	if (!priv->type_proxy) {
+		// FIXME: set a reason code
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	nm_log_dbg (LOGD_BT, "(%s): requesting connection to the device",
+	            nm_device_get_iface (device));
+
+	/* Connect to the BT device */
+	dbus_g_proxy_begin_call_with_timeout (priv->type_proxy, "Connect",
+	                                      bluez_connect_cb,
+	                                      device,
+	                                      NULL,
+	                                      20000,
+	                                      G_TYPE_STRING, dun ? BLUETOOTH_DUN_UUID : BLUETOOTH_NAP_UUID,
+	                                      G_TYPE_INVALID);
+
+	if (priv->timeout_id)
+		g_source_remove (priv->timeout_id);
+	priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device);
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+static NMActStageReturn
+real_act_stage3_ip4_config_start (NMDevice *device, NMDeviceStateReason *reason)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMActStageReturn ret;
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+		ret = nm_modem_stage3_ip4_config_start (NM_DEVICE_BT_GET_PRIVATE (device)->modem,
+		                                        device,
+		                                        NM_DEVICE_CLASS (nm_device_bt_parent_class),
+		                                        reason);
+	} else
+		ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip4_config_start (device, reason);
+
+	return ret;
+}
+
+static NMActStageReturn
+real_act_stage4_get_ip4_config (NMDevice *device,
+                                NMIP4Config **config,
+                                NMDeviceStateReason *reason)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+	NMActStageReturn ret;
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+		ret = nm_modem_stage4_get_ip4_config (NM_DEVICE_BT_GET_PRIVATE (device)->modem,
+		                                      device,
+		                                      NM_DEVICE_CLASS (nm_device_bt_parent_class),
+		                                      config,
+		                                      reason);
+	} else
+		ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage4_get_ip4_config (device, config, reason);
+
+	return ret;
+}
+
+static void
+real_deactivate_quickly (NMDevice *device)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+
+	priv->have_iface = FALSE;
+	priv->connected = FALSE;
+
+	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+
+		if (priv->modem) {
+			nm_modem_deactivate_quickly (priv->modem, device);
+
+			/* Since we're killing the Modem object before it'll get the
+			 * state change signal, simulate the state change here.
+			 */
+			nm_modem_device_state_changed (priv->modem,
+			                               NM_DEVICE_STATE_DISCONNECTED,
+			                               NM_DEVICE_STATE_ACTIVATED,
+			                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
+			g_object_unref (priv->modem);
+			priv->modem = NULL;
+		}
+
+		if (priv->type_proxy) {
+			/* Don't ever pass NULL through dbus; rfcomm_iface
+			 * might happen to be NULL for some reason.
+			 */
+			if (priv->rfcomm_iface) {
+				dbus_g_proxy_call_no_reply (priv->type_proxy, "Disconnect",
+				                            G_TYPE_STRING, priv->rfcomm_iface,
+				                            G_TYPE_INVALID);
+			}
+			g_object_unref (priv->type_proxy);
+			priv->type_proxy = NULL;
+		}
+	} else if (priv->bt_type == NM_BT_CAPABILITY_NAP) {
+		if (priv->type_proxy) {
+			dbus_g_proxy_call_no_reply (priv->type_proxy, "Disconnect",
+			                            G_TYPE_INVALID);
+			g_object_unref (priv->type_proxy);
+			priv->type_proxy = NULL;
+		}
+	}
+
+	if (priv->dev_proxy) {
+		g_object_unref (priv->dev_proxy);
+		priv->dev_proxy = NULL;
+	}
+
+	if (priv->timeout_id) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	priv->bt_type = NM_BT_CAPABILITY_NONE;
+
+	g_free (priv->rfcomm_iface);
+	priv->rfcomm_iface = NULL;
+
+	if (NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate_quickly)
+		NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate_quickly (device);
+}
+
+/*****************************************************************************/
+
+NMDevice *
+nm_device_bt_new (const char *udi,
+                  const char *bdaddr,
+                  const char *name,
+                  guint32 capabilities,
+                  gboolean managed)
+{
+	NMDevice *device;
+
+	g_return_val_if_fail (udi != NULL, NULL);
+	g_return_val_if_fail (bdaddr != NULL, NULL);
+	g_return_val_if_fail (name != NULL, NULL);
+	g_return_val_if_fail (capabilities != NM_BT_CAPABILITY_NONE, NULL);
+
+	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_BT,
+	                                    NM_DEVICE_INTERFACE_UDI, udi,
+	                                    NM_DEVICE_INTERFACE_IFACE, bdaddr,
+	                                    NM_DEVICE_INTERFACE_DRIVER, "bluez",
+	                                    NM_DEVICE_BT_HW_ADDRESS, bdaddr,
+	                                    NM_DEVICE_BT_NAME, name,
+	                                    NM_DEVICE_BT_CAPABILITIES, capabilities,
+	                                    NM_DEVICE_INTERFACE_MANAGED, managed,
+	                                    NM_DEVICE_INTERFACE_TYPE_DESC, "Bluetooth",
+	                                    NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
+	                                    NULL);
+	if (device)
+		g_signal_connect (device, "state-changed", G_CALLBACK (device_state_changed), device);
+
+	return device;
+}
+
+static void
+nm_device_bt_init (NMDeviceBt *self)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_HW_ADDRESS:
+		/* Construct only */
+		priv->bdaddr = g_ascii_strup (g_value_get_string (value), -1);
+		break;
+	case PROP_BT_NAME:
+		/* Construct only */
+		priv->name = g_value_dup_string (value);
+		break;
+	case PROP_BT_CAPABILITIES:
+		/* Construct only */
+		priv->capabilities = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_HW_ADDRESS:
+		g_value_set_string (value, priv->bdaddr);
+		break;
+	case PROP_BT_NAME:
+		g_value_set_string (value, priv->name);
+		break;
+	case PROP_BT_CAPABILITIES:
+		g_value_set_uint (value, priv->capabilities);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+	if (priv->timeout_id) {
+		g_source_remove (priv->timeout_id);
+		priv->timeout_id = 0;
+	}
+
+	if (priv->type_proxy)
+		g_object_unref (priv->type_proxy);
+
+	if (priv->dev_proxy)
+		g_object_unref (priv->dev_proxy);
+
+	if (priv->modem)
+		g_object_unref (priv->modem);
+
+	g_free (priv->rfcomm_iface);
+	g_free (priv->bdaddr);
+	g_free (priv->name);
+
+	G_OBJECT_CLASS (nm_device_bt_parent_class)->finalize (object);
+}
+
+static void
+nm_device_bt_class_init (NMDeviceBtClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMDeviceBtPrivate));
+
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->finalize = finalize;
+
+	device_class->get_best_auto_connection = real_get_best_auto_connection;
+	device_class->get_generic_capabilities = real_get_generic_capabilities;
+	device_class->connection_secrets_updated = real_connection_secrets_updated;
+	device_class->deactivate_quickly = real_deactivate_quickly;
+	device_class->act_stage2_config = real_act_stage2_config;
+	device_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start;
+	device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
+	device_class->check_connection_compatible = real_check_connection_compatible;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_HW_ADDRESS,
+		 g_param_spec_string (NM_DEVICE_BT_HW_ADDRESS,
+		                      "Bluetooth address",
+		                      "Bluetooth address",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_BT_NAME,
+		 g_param_spec_string (NM_DEVICE_BT_NAME,
+		                      "Bluetooth device name",
+		                      "Bluetooth device name",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_BT_CAPABILITIES,
+		 g_param_spec_uint (NM_DEVICE_BT_CAPABILITIES,
+		                    "Bluetooth device capabilities",
+		                    "Bluetooth device capabilities",
+		                    NM_BT_CAPABILITY_NONE, G_MAXUINT, NM_BT_CAPABILITY_NONE,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/* Signals */
+	signals[PPP_STATS] =
+		g_signal_new ("ppp-stats",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMDeviceBtClass, ppp_stats),
+		              NULL, NULL,
+		              _nm_marshal_VOID__UINT_UINT,
+		              G_TYPE_NONE, 2,
+		              G_TYPE_UINT, G_TYPE_UINT);
+
+	signals[PROPERTIES_CHANGED] = 
+		nm_properties_changed_signal_new (object_class,
+		                                  G_STRUCT_OFFSET (NMDeviceBtClass, properties_changed));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass),
+	                                 &dbus_glib_nm_device_bt_object_info);
+
+	dbus_g_error_domain_register (NM_BT_ERROR, NULL, NM_TYPE_BT_ERROR);
+}
diff --git a/src/nm-device-bt.h b/src/nm-device-bt.h
new file mode 100644
index 00000000..d3187d76
--- /dev/null
+++ b/src/nm-device-bt.h
@@ -0,0 +1,72 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_BT_H
+#define NM_DEVICE_BT_H
+
+#include <nm-device.h>
+#include "nm-modem.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_BT		(nm_device_bt_get_type ())
+#define NM_DEVICE_BT(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BT, NMDeviceBt))
+#define NM_DEVICE_BT_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_BT, NMDeviceBtClass))
+#define NM_IS_DEVICE_BT(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_BT))
+#define NM_IS_DEVICE_BT_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_BT))
+#define NM_DEVICE_BT_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_BT, NMDeviceBtClass))
+
+#define NM_DEVICE_BT_HW_ADDRESS   "hw-address"
+#define NM_DEVICE_BT_NAME         "name"
+#define NM_DEVICE_BT_CAPABILITIES "bt-capabilities"
+
+typedef struct {
+	NMDevice parent;
+} NMDeviceBt;
+
+typedef struct {
+	NMDeviceClass parent;
+
+	/* Signals */
+	void (*ppp_stats) (NMDeviceBt *device, guint32 in_bytes, guint32 out_bytes);
+	void (*properties_changed) (NMDeviceBt *device, GHashTable *properties);
+} NMDeviceBtClass;
+
+GType nm_device_bt_get_type (void);
+
+NMDevice *nm_device_bt_new (const char *udi,
+                            const char *bdaddr,
+                            const char *name,
+                            guint32 capabilities,
+                            gboolean managed);
+
+guint32 nm_device_bt_get_capabilities (NMDeviceBt *device);
+
+const char *nm_device_bt_get_hw_address (NMDeviceBt *device);
+
+gboolean nm_device_bt_modem_added (NMDeviceBt *device,
+                                   NMModem *modem,
+                                   const char *driver);
+
+gboolean nm_device_bt_modem_removed (NMDeviceBt *device, NMModem *modem);
+
+G_END_DECLS
+
+#endif /* NM_GSM_DEVICE_H */
diff --git a/src/nm-device-cdma.c b/src/nm-device-cdma.c
new file mode 100644
index 00000000..45771f42
--- /dev/null
+++ b/src/nm-device-cdma.c
@@ -0,0 +1,75 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-modem-cdma.h"
+#include "nm-device-interface.h"
+#include "nm-device-cdma.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-rfkill.h"
+
+#include "nm-device-cdma-glue.h"
+
+G_DEFINE_TYPE (NMDeviceCdma, nm_device_cdma, NM_TYPE_DEVICE_MODEM)
+
+enum {
+	PROPERTIES_CHANGED,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+NMDevice *
+nm_device_cdma_new (NMModemCdma *modem, const char *driver)
+{
+	g_return_val_if_fail (modem != NULL, NULL);
+	g_return_val_if_fail (NM_IS_MODEM_CDMA (modem), NULL);
+	g_return_val_if_fail (driver != NULL, NULL);
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_CDMA,
+	                                  NM_DEVICE_INTERFACE_UDI, nm_modem_get_path (NM_MODEM (modem)),
+	                                  NM_DEVICE_INTERFACE_IFACE, nm_modem_get_iface (NM_MODEM (modem)),
+	                                  NM_DEVICE_INTERFACE_DRIVER, driver,
+	                                  NM_DEVICE_INTERFACE_TYPE_DESC, "CDMA",
+	                                  NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_CDMA,
+	                                  NM_DEVICE_INTERFACE_RFKILL_TYPE, RFKILL_TYPE_WWAN,
+	                                  NM_DEVICE_MODEM_MODEM, modem,
+	                                  NULL);
+}
+
+static void
+nm_device_cdma_init (NMDeviceCdma *self)
+{
+}
+
+static void
+nm_device_cdma_class_init (NMDeviceCdmaClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	/* Signals */
+	signals[PROPERTIES_CHANGED] = 
+		nm_properties_changed_signal_new (object_class,
+		                                  G_STRUCT_OFFSET (NMDeviceCdmaClass, properties_changed));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass),
+	                                 &dbus_glib_nm_device_cdma_object_info);
+}
+
diff --git a/src/nm-device-cdma.h b/src/nm-device-cdma.h
new file mode 100644
index 00000000..f0bf3456
--- /dev/null
+++ b/src/nm-device-cdma.h
@@ -0,0 +1,55 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_CDMA_H
+#define NM_DEVICE_CDMA_H
+
+#include "nm-device-modem.h"
+#include "nm-modem-cdma.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_CDMA            (nm_device_cdma_get_type ())
+#define NM_DEVICE_CDMA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_CDMA, NMDeviceCdma))
+#define NM_DEVICE_CDMA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_CDMA, NMDeviceCdmaClass))
+#define NM_IS_DEVICE_CDMA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_CDMA))
+#define NM_IS_DEVICE_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_CDMA))
+#define NM_DEVICE_CDMA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_CDMA, NMDeviceCdmaClass))
+
+typedef struct {
+	NMDeviceModem parent;
+} NMDeviceCdma;
+
+typedef struct {
+	NMDeviceModemClass parent;
+
+	/* Signals */
+	void (*signal_quality) (NMDeviceCdma *self, guint32 quality);
+
+	void (*properties_changed) (NMDeviceCdma *self, GHashTable *properties);
+} NMDeviceCdmaClass;
+
+GType nm_device_cdma_get_type (void);
+
+NMDevice *nm_device_cdma_new (NMModemCdma *modem, const char *driver);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_CDMA_H */
diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c
new file mode 100644
index 00000000..be6d4e2f
--- /dev/null
+++ b/src/nm-device-ethernet.c
@@ -0,0 +1,1975 @@
+/* -*- 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 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <net/ethernet.h>
+#include <stdlib.h>
+#include <linux/types.h>
+#include <linux/sockios.h>
+#include <linux/version.h>
+#include <linux/ethtool.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <linux/if.h>
+#include <errno.h>
+
+#include <netlink/route/addr.h>
+
+#include "nm-glib-compat.h"
+#include "nm-device-ethernet.h"
+#include "nm-device-interface.h"
+#include "nm-device-private.h"
+#include "nm-activation-request.h"
+#include "NetworkManagerUtils.h"
+#include "nm-supplicant-manager.h"
+#include "nm-supplicant-interface.h"
+#include "nm-supplicant-config.h"
+#include "nm-netlink-monitor.h"
+#include "nm-system.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-8021x.h"
+#include "nm-setting-pppoe.h"
+#include "ppp-manager/nm-ppp-manager.h"
+#include "nm-logging.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-dhcp-manager.h"
+
+#include "nm-device-ethernet-glue.h"
+
+
+G_DEFINE_TYPE (NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_ETHERNET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetPrivate))
+
+#define WIRED_SECRETS_TRIES "wired-secrets-tries"
+
+typedef enum
+{
+	NM_ETHERNET_ERROR_CONNECTION_NOT_WIRED = 0,
+	NM_ETHERNET_ERROR_CONNECTION_INVALID,
+	NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE,
+} NMEthernetError;
+
+#define NM_ETHERNET_ERROR (nm_ethernet_error_quark ())
+#define NM_TYPE_ETHERNET_ERROR (nm_ethernet_error_get_type ()) 
+
+typedef struct SupplicantStateTask {
+	NMDeviceEthernet *self;
+	guint32 new_state;
+	guint32 old_state;
+	gboolean mgr_task;
+	guint source_id;
+} SupplicantStateTask;
+
+typedef struct Supplicant {
+	NMSupplicantManager *mgr;
+	NMSupplicantInterface *iface;
+
+	/* signal handler ids */
+	guint mgr_state_id;
+	guint iface_error_id;
+	guint iface_state_id;
+	guint iface_con_state_id;
+
+	/* Timeouts and idles */
+	guint iface_con_error_cb_id;
+	guint con_timeout_id;
+
+	GSList *iface_tasks;
+	GSList *mgr_tasks;
+} Supplicant;
+
+typedef struct {
+	gboolean	disposed;
+
+	struct ether_addr	hw_addr;
+	gboolean			carrier;
+
+	NMNetlinkMonitor *  monitor;
+	gulong              link_connected_id;
+	gulong              link_disconnected_id;
+	guint               carrier_action_defer_id;
+
+	Supplicant          supplicant;
+	guint               supplicant_timeout_id;
+
+	/* PPPoE */
+	NMPPPManager *ppp_manager;
+	NMIP4Config  *pending_ip4_config;
+} NMDeviceEthernetPrivate;
+
+enum {
+	PROPERTIES_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_0,
+	PROP_HW_ADDRESS,
+	PROP_SPEED,
+	PROP_CARRIER,
+
+	LAST_PROP
+};
+
+
+static gboolean supports_mii_carrier_detect (NMDeviceEthernet *dev);
+static gboolean supports_ethtool_carrier_detect (NMDeviceEthernet *dev);
+
+static GQuark
+nm_ethernet_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-ethernet-error");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+static GType
+nm_ethernet_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Connection was not a wired connection. */
+			ENUM_ENTRY (NM_ETHERNET_ERROR_CONNECTION_NOT_WIRED, "ConnectionNotWired"),
+			/* Connection was not a valid wired connection. */
+			ENUM_ENTRY (NM_ETHERNET_ERROR_CONNECTION_INVALID, "ConnectionInvalid"),
+			/* Connection does not apply to this device. */
+			ENUM_ENTRY (NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMEthernetError", values);
+	}
+	return etype;
+}
+
+static void
+carrier_action_defer_clear (NMDeviceEthernet *self)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	if (priv->carrier_action_defer_id) {
+		g_source_remove (priv->carrier_action_defer_id);
+		priv->carrier_action_defer_id = 0;
+	}
+}
+
+static gboolean
+carrier_action_defer_cb (gpointer user_data)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	NMDeviceState state;
+
+	priv->carrier_action_defer_id = 0;
+
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
+	if (state == NM_DEVICE_STATE_UNAVAILABLE) {
+		if (priv->carrier)
+			nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER);
+	} else if (state >= NM_DEVICE_STATE_DISCONNECTED) {
+		if (!priv->carrier)
+			nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER);
+	}
+
+	return FALSE;
+}
+
+static void
+set_carrier (NMDeviceEthernet *self,
+             const gboolean carrier,
+             const gboolean defer_action)
+{
+	NMDeviceEthernetPrivate *priv;
+	NMDeviceState state;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	if (priv->carrier == carrier)
+		return;
+
+	/* Clear any previous deferred action */
+	carrier_action_defer_clear (self);
+
+	priv->carrier = carrier;
+	g_object_notify (G_OBJECT (self), NM_DEVICE_ETHERNET_CARRIER);
+
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
+	nm_log_info (LOGD_HW | LOGD_ETHER, "(%s): carrier now %s (device state %d%s)",
+	             nm_device_get_iface (NM_DEVICE (self)),
+	             carrier ? "ON" : "OFF",
+	             state,
+	             defer_action ? ", deferring action for 4 seconds" : "");
+
+	if (defer_action)
+		priv->carrier_action_defer_id = g_timeout_add_seconds (4, carrier_action_defer_cb, self);
+	else
+		carrier_action_defer_cb (self);
+}
+
+static void
+carrier_on (NMNetlinkMonitor *monitor,
+            int idx,
+            gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	guint32 caps;
+
+	/* Make sure signal is for us */
+	if (idx == nm_device_get_ifindex (device)) {
+		/* Ignore spurious netlink messages */
+		caps = nm_device_get_capabilities (device);
+		if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT))
+			return;
+
+		set_carrier (self, TRUE, FALSE);
+	}
+}
+
+static void
+carrier_off (NMNetlinkMonitor *monitor,
+             int idx,
+             gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	guint32 caps;
+
+	/* Make sure signal is for us */
+	if (idx == nm_device_get_ifindex (device)) {
+		NMDeviceState state;
+		gboolean defer = FALSE;
+
+		/* Ignore spurious netlink messages */
+		caps = nm_device_get_capabilities (device);
+		if (!(caps & NM_DEVICE_CAP_CARRIER_DETECT))
+			return;
+
+		/* Defer carrier-off event actions while connected by a few seconds
+		 * so that tripping over a cable, power-cycling a switch, or breaking
+		 * off the RJ45 locking tab isn't so catastrophic.
+		 */
+		state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
+		if (state > NM_DEVICE_STATE_DISCONNECTED)
+			defer = TRUE;
+
+		set_carrier (self, FALSE, defer);
+	}
+}
+
+static GObject*
+constructor (GType type,
+			 guint n_construct_params,
+			 GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	NMDeviceEthernetPrivate *priv;
+	NMDevice *self;
+	guint32 caps;
+
+	object = G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructor (type,
+	                                                                        n_construct_params,
+	                                                                        construct_params);
+	if (!object)
+		return NULL;
+
+	self = NM_DEVICE (object);
+	priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	nm_log_dbg (LOGD_HW | LOGD_OLPC_MESH, "(%s): kernel ifindex %d",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            nm_device_get_ifindex (NM_DEVICE (self)));
+
+	caps = nm_device_get_capabilities (self);
+	if (caps & NM_DEVICE_CAP_CARRIER_DETECT) {
+		GError *error = NULL;
+		guint32 ifflags = 0;
+
+		/* Only listen to netlink for cards that support carrier detect */
+		priv->monitor = nm_netlink_monitor_get ();
+
+		priv->link_connected_id = g_signal_connect (priv->monitor, "carrier-on",
+		                                            G_CALLBACK (carrier_on),
+		                                            self);
+		priv->link_disconnected_id = g_signal_connect (priv->monitor, "carrier-off",
+		                                               G_CALLBACK (carrier_off),
+		                                               self);
+
+		/* Get initial link state */
+		if (!nm_netlink_monitor_get_flags_sync (priv->monitor,
+		                                        nm_device_get_ifindex (NM_DEVICE (self)),
+		                                        &ifflags,
+		                                        &error)) {
+			nm_log_warn (LOGD_HW | LOGD_ETHER,
+			             "(%s): couldn't get initial carrier state: (%d) %s",
+			             nm_device_get_iface (NM_DEVICE (self)),
+			             error ? error->code : -1,
+			             (error && error->message) ? error->message : "unknown");
+			g_clear_error (&error);
+		} else
+			priv->carrier = !!(ifflags & IFF_LOWER_UP);
+
+		nm_log_info (LOGD_HW | LOGD_ETHER,
+		             "(%s): carrier is %s",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             priv->carrier ? "ON" : "OFF");
+
+		/* Request link state again just in case an error occurred getting the
+		 * initial link state.
+		 */
+		if (!nm_netlink_monitor_request_status (priv->monitor, &error)) {
+			nm_log_warn (LOGD_HW | LOGD_ETHER,
+			             "(%s): couldn't request carrier state: (%d) %s",
+			             nm_device_get_iface (NM_DEVICE (self)),
+			             error ? error->code : -1,
+			             (error && error->message) ? error->message : "unknown");
+			g_clear_error (&error);
+		}
+	} else {
+		nm_log_info (LOGD_HW | LOGD_ETHER,
+		             "(%s): driver '%s' does not support carrier detection.",
+		             nm_device_get_iface (self),
+		             nm_device_get_driver (self));
+		priv->carrier = TRUE;
+	}
+
+	return object;
+}
+
+static void
+nm_device_ethernet_init (NMDeviceEthernet * self)
+{
+}
+
+static gboolean
+real_is_up (NMDevice *device)
+{
+	if (!NM_DEVICE_ETHERNET_GET_PRIVATE (device)->supplicant.mgr)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+real_bring_up (NMDevice *dev)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (dev);
+
+	priv->supplicant.mgr = nm_supplicant_manager_get ();
+
+	return priv->supplicant.mgr ? TRUE : FALSE;
+}
+
+static void
+real_take_down (NMDevice *dev)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (dev);
+
+	if (priv->supplicant.mgr) {
+		g_object_unref (priv->supplicant.mgr);
+		priv->supplicant.mgr = NULL;
+	}
+}
+
+static gboolean
+real_hw_is_up (NMDevice *device)
+{
+	return nm_system_device_is_up (device);
+}
+
+static gboolean
+real_hw_bring_up (NMDevice *dev, gboolean *no_firmware)
+{
+	return nm_system_device_set_up_down (dev, TRUE, no_firmware);
+}
+
+static void
+real_hw_take_down (NMDevice *dev)
+{
+	nm_system_device_set_up_down (dev, FALSE, NULL);
+}
+
+NMDevice *
+nm_device_ethernet_new (const char *udi,
+						const char *iface,
+						const char *driver)
+{
+	g_return_val_if_fail (udi != NULL, NULL);
+	g_return_val_if_fail (iface != NULL, NULL);
+	g_return_val_if_fail (driver != NULL, NULL);
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ETHERNET,
+	                                  NM_DEVICE_INTERFACE_UDI, udi,
+	                                  NM_DEVICE_INTERFACE_IFACE, iface,
+	                                  NM_DEVICE_INTERFACE_DRIVER, driver,
+	                                  NM_DEVICE_INTERFACE_TYPE_DESC, "Ethernet",
+	                                  NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
+	                                  NULL);
+}
+
+
+/*
+ * nm_device_ethernet_get_address
+ *
+ * Get a device's hardware address
+ *
+ */
+void
+nm_device_ethernet_get_address (NMDeviceEthernet *self, struct ether_addr *addr)
+{
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (addr != NULL);
+
+	memcpy (addr, &(NM_DEVICE_ETHERNET_GET_PRIVATE (self)->hw_addr), sizeof (struct ether_addr));
+}
+
+/* Returns speed in Mb/s */
+static guint32
+nm_device_ethernet_get_speed (NMDeviceEthernet *self)
+{
+	int fd;
+	struct ifreq ifr;
+	struct ethtool_cmd edata = {
+		.cmd = ETHTOOL_GSET,
+	};
+	guint32 speed = 0;
+
+	g_return_val_if_fail (self != NULL, 0);
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_warn (LOGD_HW, "couldn't open control socket.");
+		return 0;
+	}
+
+	memset (&ifr, 0, sizeof (struct ifreq));
+	strncpy (ifr.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ);
+	ifr.ifr_data = (char *) &edata;
+
+	if (ioctl (fd, SIOCETHTOOL, &ifr) < 0)
+		goto out;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+	speed = edata.speed;
+#else
+	speed = ethtool_cmd_speed (&edata);
+#endif
+
+	if (speed == G_MAXUINT16 || speed == G_MAXUINT32)
+		speed = 0;
+
+out:
+	close (fd);
+	return speed;
+}
+
+static void
+real_update_hw_address (NMDevice *dev)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	struct ifreq req;
+	int fd;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_warn (LOGD_HW, "couldn't open control socket.");
+		return;
+	}
+
+	memset (&req, 0, sizeof (struct ifreq));
+	strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+	if (ioctl (fd, SIOCGIFHWADDR, &req) < 0) {
+		nm_log_err (LOGD_HW | LOGD_ETHER,
+		            "(%s) error getting hardware address: %d",
+		            nm_device_get_iface (dev), errno);
+		goto out;
+	}
+
+	if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) {
+		memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr));
+		g_object_notify (G_OBJECT (dev), NM_DEVICE_ETHERNET_HW_ADDRESS);
+	}
+
+out:
+	close (fd);
+}
+
+static guint32
+real_get_generic_capabilities (NMDevice *dev)
+{
+	NMDeviceEthernet *	self = NM_DEVICE_ETHERNET (dev);
+	guint32		caps = NM_DEVICE_CAP_NONE;
+
+	/* cipsec devices are also explicitly unsupported at this time */
+	if (strstr (nm_device_get_iface (dev), "cipsec"))
+		return NM_DEVICE_CAP_NONE;
+
+	if (supports_ethtool_carrier_detect (self) || supports_mii_carrier_detect (self))
+		caps |= NM_DEVICE_CAP_CARRIER_DETECT;
+
+	caps |= NM_DEVICE_CAP_NM_SUPPORTED;
+
+	return caps;
+}
+
+static gboolean
+real_can_interrupt_activation (NMDevice *dev)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+	gboolean interrupt = FALSE;
+
+	/* Devices that support carrier detect can interrupt activation
+	 * if the link becomes inactive.
+	 */
+	if (nm_device_get_capabilities (dev) & NM_DEVICE_CAP_CARRIER_DETECT) {
+		if (NM_DEVICE_ETHERNET_GET_PRIVATE (self)->carrier == FALSE)
+			interrupt = TRUE;
+	}
+	return interrupt;
+}
+
+static gboolean
+real_is_available (NMDevice *dev)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+
+	/* Can't do anything if there isn't a carrier */
+	if (!NM_DEVICE_ETHERNET_GET_PRIVATE (self)->carrier)
+		return FALSE;
+
+	return TRUE;
+}
+
+static NMConnection *
+real_get_best_auto_connection (NMDevice *dev,
+                               GSList *connections,
+                               char **specific_object)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	GSList *iter;
+
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		NMConnection *connection = NM_CONNECTION (iter->data);
+		NMSettingConnection *s_con;
+		NMSettingWired *s_wired;
+		const char *connection_type;
+		gboolean is_pppoe = FALSE;
+
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+		g_assert (s_con);
+
+		connection_type = nm_setting_connection_get_connection_type (s_con);
+		if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME))
+			is_pppoe = TRUE;
+
+		if (!is_pppoe && strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME))
+			continue;
+		if (!nm_setting_connection_get_autoconnect (s_con))
+			continue;
+
+		s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+		/* Wired setting optional for PPPoE */
+		if (!is_pppoe && !s_wired)
+			continue;
+
+		if (s_wired) {
+			const GByteArray *mac;
+
+			mac = nm_setting_wired_get_mac_address (s_wired);
+			if (mac && memcmp (mac->data, priv->hw_addr.ether_addr_octet, ETH_ALEN))
+				continue;
+		}
+
+		return connection;
+	}
+	return NULL;
+}
+
+static void
+real_connection_secrets_updated (NMDevice *dev,
+                                 NMConnection *connection,
+                                 GSList *updated_settings,
+                                 RequestSecretsCaller caller)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (dev);
+	NMActRequest *req;
+	gboolean valid = FALSE;
+	GSList *iter;
+
+	g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (dev)));
+
+	/* PPPoE? */
+	if (caller == SECRETS_CALLER_PPP) {
+		NMSettingPPPOE *s_pppoe;
+
+		g_assert (priv->ppp_manager);
+
+		s_pppoe = (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
+		if (!s_pppoe) {
+			nm_ppp_manager_update_secrets (priv->ppp_manager,
+			                               nm_device_get_iface (dev),
+			                               NULL,
+			                               NULL,
+			                               "missing PPPoE setting; no secrets could be found.");
+		} else {
+			const char *pppoe_username = nm_setting_pppoe_get_username (s_pppoe);
+			const char *pppoe_password = nm_setting_pppoe_get_password (s_pppoe);
+
+			nm_ppp_manager_update_secrets (priv->ppp_manager,
+			                               nm_device_get_iface (dev),
+			                               pppoe_username ? pppoe_username : "",
+			                               pppoe_password ? pppoe_password : "",
+			                               NULL);
+		}
+		return;
+	}
+
+	/* Only caller could be ourselves for 802.1x */
+	g_return_if_fail (caller == SECRETS_CALLER_ETHERNET);
+	g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH);
+
+	for (iter = updated_settings; iter; iter = g_slist_next (iter)) {
+		const char *setting_name = (const char *) iter->data;
+
+		if (!strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME)) {
+			valid = TRUE;
+		} else {
+			nm_log_warn (LOGD_DEVICE, "Ignoring updated secrets for setting '%s'.",
+			             setting_name);
+		}
+	}
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	g_return_if_fail (nm_act_request_get_connection (req) == connection);
+	nm_device_activate_schedule_stage1_device_prepare (dev);
+}
+
+/* FIXME: Move it to nm-device.c and then get rid of all foo_device_get_setting() all around.
+   It's here now to keep the patch short. */
+static NMSetting *
+device_get_setting (NMDevice *device, GType setting_type)
+{
+	NMActRequest *req;
+	NMSetting *setting = NULL;
+
+	req = nm_device_get_act_request (device);
+	if (req) {
+		NMConnection *connection;
+
+		connection = nm_act_request_get_connection (req);
+		if (connection)
+			setting = nm_connection_get_setting (connection, setting_type);
+	}
+
+	return setting;
+}
+
+/*****************************************************************************/
+/* 802.1X */
+
+static void
+remove_supplicant_timeouts (NMDeviceEthernet *self)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	if (priv->supplicant.con_timeout_id) {
+		g_source_remove (priv->supplicant.con_timeout_id);
+		priv->supplicant.con_timeout_id = 0;
+	}
+
+	if (priv->supplicant_timeout_id) {
+		g_source_remove (priv->supplicant_timeout_id);
+		priv->supplicant_timeout_id = 0;
+	}
+}
+
+static void
+finish_supplicant_task (SupplicantStateTask *task, gboolean remove_source)
+{
+	NMDeviceEthernet *self = task->self;
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	/* idle/timeout handlers should pass FALSE for remove_source, since they
+	 * will tell glib to remove their source from the mainloop by returning
+	 * FALSE when they exit.  When called from this NMDevice's dispose handler,
+	 * remove_source should be TRUE to cancel all outstanding idle/timeout
+	 * handlers asynchronously.
+	 */
+	if (task->source_id && remove_source)
+		g_source_remove (task->source_id);
+
+	if (task->mgr_task)
+		priv->supplicant.mgr_tasks = g_slist_remove (priv->supplicant.mgr_tasks, task);
+	else
+		priv->supplicant.iface_tasks = g_slist_remove (priv->supplicant.iface_tasks, task);
+
+	memset (task, 0, sizeof (SupplicantStateTask));
+	g_slice_free (SupplicantStateTask, task);
+}
+
+static void
+remove_supplicant_interface_error_handler (NMDeviceEthernet *self)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	if (priv->supplicant.iface_error_id != 0) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_error_id);
+		priv->supplicant.iface_error_id = 0;
+	}
+
+	if (priv->supplicant.iface_con_error_cb_id > 0) {
+		g_source_remove (priv->supplicant.iface_con_error_cb_id);
+		priv->supplicant.iface_con_error_cb_id = 0;
+	}
+}
+
+static void
+supplicant_interface_release (NMDeviceEthernet *self)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	remove_supplicant_timeouts (self);
+	remove_supplicant_interface_error_handler (self);
+
+	/* Clean up all pending supplicant interface state idle tasks */
+	while (priv->supplicant.iface_tasks)
+		finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE);
+
+	if (priv->supplicant.iface_con_state_id) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_con_state_id);
+		priv->supplicant.iface_con_state_id = 0;
+	}
+
+	if (priv->supplicant.iface_state_id > 0) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_state_id);
+		priv->supplicant.iface_state_id = 0;
+	}
+
+	if (priv->supplicant.mgr_state_id) {
+		g_signal_handler_disconnect (priv->supplicant.mgr, priv->supplicant.mgr_state_id);
+		priv->supplicant.mgr_state_id = 0;
+	}
+
+	if (priv->supplicant.iface) {
+		nm_supplicant_interface_disconnect (priv->supplicant.iface);
+		nm_supplicant_manager_release_iface (priv->supplicant.mgr, priv->supplicant.iface);
+		priv->supplicant.iface = NULL;
+	}
+}
+
+static gboolean
+link_timeout_cb (gpointer user_data)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	NMDevice *dev = NM_DEVICE (self);
+	NMActRequest *req;
+	NMConnection *connection;
+	const char *setting_name;
+
+	priv->supplicant_timeout_id = 0;
+
+	req = nm_device_get_act_request (dev);
+
+	if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) {
+		nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+		return FALSE;
+	}
+
+	/* Disconnect event during initial authentication and credentials
+	 * ARE checked - we are likely to have wrong key.  Ask the user for
+	 * another one.
+	 */
+	if (nm_device_get_state (dev) != NM_DEVICE_STATE_CONFIG)
+		goto time_out;
+
+	connection = nm_act_request_get_connection (req);
+	nm_connection_clear_secrets (connection);
+	setting_name = nm_connection_need_secrets (connection, NULL);
+	if (!setting_name)
+		goto time_out;
+
+	nm_log_info (LOGD_DEVICE | LOGD_ETHER,
+	             "Activation (%s/wired): disconnected during authentication,"
+	             " asking for new key.",
+	             nm_device_get_iface (dev));
+	supplicant_interface_release (self);
+
+	nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+	nm_act_request_get_secrets (req,
+	                            setting_name,
+	                            TRUE,
+	                            SECRETS_CALLER_ETHERNET,
+	                            NULL,
+	                            NULL);
+
+	return FALSE;
+
+time_out:
+	nm_log_warn (LOGD_DEVICE | LOGD_ETHER,
+	             "(%s): link timed out.", nm_device_get_iface (dev));
+	nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+
+	return FALSE;
+}
+
+static gboolean
+schedule_state_handler (NMDeviceEthernet *self,
+                        GSourceFunc handler,
+                        guint32 new_state,
+                        guint32 old_state,
+                        gboolean mgr_task)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	SupplicantStateTask *task;
+
+	if (new_state == old_state)
+		return TRUE;
+
+	task = g_slice_new0 (SupplicantStateTask);
+	if (!task) {
+		nm_log_err (LOGD_DEVICE, "Not enough memory to process supplicant manager state change.");
+		return FALSE;
+	}
+
+	task->self = self;
+	task->new_state = new_state;
+	task->old_state = old_state;
+	task->mgr_task = mgr_task;
+
+	task->source_id = g_idle_add (handler, task);
+	if (mgr_task)
+		priv->supplicant.mgr_tasks = g_slist_append (priv->supplicant.mgr_tasks, task);
+	else
+		priv->supplicant.iface_tasks = g_slist_append (priv->supplicant.iface_tasks, task);
+	return TRUE;
+}
+
+static gboolean
+supplicant_mgr_state_cb_handler (gpointer user_data)
+{
+	SupplicantStateTask *task = (SupplicantStateTask *) user_data;
+	NMDevice *device = NM_DEVICE (task->self);
+
+	/* If the supplicant went away, release the supplicant interface */
+	if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) {
+		supplicant_interface_release (task->self);
+
+		if (nm_device_get_state (device) > NM_DEVICE_STATE_UNAVAILABLE) {
+			nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE,
+			                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		}
+	}
+
+	finish_supplicant_task (task, FALSE);
+	return FALSE;
+}
+
+static void
+supplicant_mgr_state_cb (NMSupplicantInterface * iface,
+                         guint32 new_state,
+                         guint32 old_state,
+                         gpointer user_data)
+{
+	nm_log_info (LOGD_DEVICE | LOGD_ETHER,
+	             "(%s): supplicant manager state:  %s -> %s",
+	             nm_device_get_iface (NM_DEVICE (user_data)),
+	             nm_supplicant_manager_state_to_string (old_state),
+	             nm_supplicant_manager_state_to_string (new_state));
+
+	schedule_state_handler (NM_DEVICE_ETHERNET (user_data),
+	                        supplicant_mgr_state_cb_handler,
+	                        new_state,
+	                        old_state,
+	                        TRUE);
+}
+
+static NMSupplicantConfig *
+build_supplicant_config (NMDeviceEthernet *self)
+{
+	const char *con_path;
+	NMSupplicantConfig *config = NULL;
+	NMSetting8021x *security;
+	NMConnection *connection;
+
+	connection = nm_act_request_get_connection (nm_device_get_act_request (NM_DEVICE (self)));
+	g_return_val_if_fail (connection, NULL);
+	con_path = nm_connection_get_path (connection);
+
+	config = nm_supplicant_config_new ();
+	if (!config)
+		return NULL;
+
+	security = NM_SETTING_802_1X (nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X));
+	if (!nm_supplicant_config_add_setting_8021x (config, security, con_path, TRUE)) {
+		nm_log_warn (LOGD_DEVICE, "Couldn't add 802.1X security setting to supplicant config.");
+		g_object_unref (config);
+		config = NULL;
+	}
+
+	return config;
+}
+
+static gboolean
+supplicant_iface_state_cb_handler (gpointer user_data)
+{
+	SupplicantStateTask *task = (SupplicantStateTask *) user_data;
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (task->self);
+	NMDevice *device = NM_DEVICE (task->self);
+
+	if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) {
+		NMSupplicantConfig *config;
+		const char *iface;
+		gboolean success = FALSE;
+
+		iface = nm_device_get_iface (device);
+		config = build_supplicant_config (task->self);
+		if (config) {
+			success = nm_supplicant_interface_set_config (priv->supplicant.iface, config);
+			g_object_unref (config);
+
+			if (!success) {
+				nm_log_err (LOGD_DEVICE | LOGD_ETHER,
+				            "Activation (%s/wired): couldn't send security "
+						    "configuration to the supplicant.",
+						    iface);
+			}
+		} else {
+			nm_log_warn (LOGD_DEVICE | LOGD_ETHER,
+			             "Activation (%s/wired): couldn't build security configuration.",
+			             iface);
+		}
+
+		if (!success)
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+	} else if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
+		NMDeviceState state = nm_device_get_state (device);
+
+		supplicant_interface_release (task->self);
+
+		if (nm_device_is_activating (device) || state == NM_DEVICE_STATE_ACTIVATED)
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+	}
+
+	finish_supplicant_task (task, FALSE);
+	return FALSE;
+}
+
+static void
+supplicant_iface_state_cb (NMSupplicantInterface * iface,
+                           guint32 new_state,
+                           guint32 old_state,
+                           gpointer user_data)
+{
+
+	nm_log_info (LOGD_DEVICE | LOGD_ETHER,
+	             "(%s): supplicant interface state:  %s -> %s",
+	             nm_device_get_iface (NM_DEVICE (user_data)),
+	             nm_supplicant_interface_state_to_string (old_state),
+	             nm_supplicant_interface_state_to_string (new_state));
+
+	schedule_state_handler (NM_DEVICE_ETHERNET (user_data),
+	                        supplicant_iface_state_cb_handler,
+	                        new_state,
+	                        old_state,
+	                        FALSE);
+}
+
+static gboolean
+supplicant_iface_connection_state_cb_handler (gpointer user_data)
+{
+	SupplicantStateTask *task = (SupplicantStateTask *) user_data;
+	NMDevice *dev = NM_DEVICE (task->self);
+
+	if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) {
+		remove_supplicant_interface_error_handler (task->self);
+		remove_supplicant_timeouts (task->self);
+
+		/* If this is the initial association during device activation,
+		 * schedule the next activation stage.
+		 */
+		if (nm_device_get_state (dev) == NM_DEVICE_STATE_CONFIG) {
+			nm_log_info (LOGD_DEVICE | LOGD_ETHER,
+			             "Activation (%s/wired) Stage 2 of 5 (Device Configure) successful.",
+				         nm_device_get_iface (dev));
+			nm_device_activate_schedule_stage3_ip_config_start (dev);
+		}
+	} else if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) {
+		if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (dev)) {
+			NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (task->self);
+
+			/* Start the link timeout so we allow some time for reauthentication */
+			if (!priv->supplicant_timeout_id)
+				priv->supplicant_timeout_id = g_timeout_add_seconds (15, link_timeout_cb, dev);
+		}
+	}
+
+	finish_supplicant_task (task, FALSE);
+	return FALSE;
+}
+
+static void
+supplicant_iface_connection_state_cb (NMSupplicantInterface * iface,
+                                      guint32 new_state,
+                                      guint32 old_state,
+                                      gpointer user_data)
+{
+	nm_log_info (LOGD_DEVICE | LOGD_ETHER,
+	             "(%s) supplicant connection state:  %s -> %s",
+	             nm_device_get_iface (NM_DEVICE (user_data)),
+	             nm_supplicant_interface_connection_state_to_string (old_state),
+	             nm_supplicant_interface_connection_state_to_string (new_state));
+
+	schedule_state_handler (NM_DEVICE_ETHERNET (user_data),
+	                        supplicant_iface_connection_state_cb_handler,
+	                        new_state,
+	                        old_state,
+	                        FALSE);
+}
+
+static gboolean
+supplicant_iface_connection_error_cb_handler (gpointer user_data)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	supplicant_interface_release (self);
+	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+
+	priv->supplicant.iface_con_error_cb_id = 0;
+	return FALSE;
+}
+
+static void
+supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
+                                      const char *name,
+                                      const char *message,
+                                      gpointer user_data)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	guint id;
+
+	nm_log_warn (LOGD_DEVICE | LOGD_ETHER,
+	             "Activation (%s/wired): association request to the supplicant failed: %s - %s",
+	             nm_device_get_iface (NM_DEVICE (self)), name, message);
+
+	if (priv->supplicant.iface_con_error_cb_id)
+		g_source_remove (priv->supplicant.iface_con_error_cb_id);
+
+	id = g_idle_add (supplicant_iface_connection_error_cb_handler, self);
+	priv->supplicant.iface_con_error_cb_id = id;
+}
+
+static NMActStageReturn
+handle_auth_or_fail (NMDeviceEthernet *self,
+                     NMActRequest *req,
+                     gboolean new_secrets)
+{
+	const char *setting_name;
+	guint32 tries;
+	NMConnection *connection;
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), WIRED_SECRETS_TRIES));
+	if (tries > 3)
+		return NM_ACT_STAGE_RETURN_FAILURE;
+
+	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
+
+	nm_connection_clear_secrets (connection);
+	setting_name = nm_connection_need_secrets (connection, NULL);
+	if (setting_name) {
+		gboolean get_new;
+
+		/* If the caller doesn't necessarily want completely new secrets,
+		 * only ask for new secrets after the first failure.
+		 */
+		get_new = new_secrets ? TRUE : (tries ? TRUE : FALSE);
+		nm_act_request_get_secrets (req,
+		                            setting_name,
+		                            get_new,
+		                            SECRETS_CALLER_ETHERNET,
+		                            NULL,
+		                            NULL);
+
+		g_object_set_data (G_OBJECT (connection), WIRED_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
+	} else {
+		nm_log_info (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
+	}
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+static gboolean
+supplicant_connection_timeout_cb (gpointer user_data)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+	NMActRequest *req;
+	const char *iface;
+
+	priv->supplicant.con_timeout_id = 0;
+
+	iface = nm_device_get_iface (device);
+
+	/* Authentication failed, encryption key is probably bad */
+	nm_log_warn (LOGD_DEVICE | LOGD_ETHER,
+	             "Activation (%s/wired): association took too long.", iface);
+
+	supplicant_interface_release (self);
+	req = nm_device_get_act_request (device);
+	g_assert (req);
+
+	if (handle_auth_or_fail (self, req, TRUE) == NM_ACT_STAGE_RETURN_POSTPONE) {
+		nm_log_info (LOGD_DEVICE | LOGD_ETHER,
+		             "Activation (%s/wired): asking for new secrets", iface);
+	} else
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS);
+
+	return FALSE;
+}
+
+static gboolean
+supplicant_interface_init (NMDeviceEthernet *self)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	const char *iface;
+
+	iface = nm_device_get_iface (NM_DEVICE (self));
+
+	/* Create supplicant interface */
+	priv->supplicant.iface = nm_supplicant_manager_get_iface (priv->supplicant.mgr, iface, FALSE);
+	if (!priv->supplicant.iface) {
+		nm_log_err (LOGD_DEVICE | LOGD_ETHER,
+		            "Couldn't initialize supplicant interface for %s.",
+		            iface);
+		supplicant_interface_release (self);
+
+		return FALSE;
+	}
+
+	/* Listen for it's state signals */
+	priv->supplicant.iface_state_id = g_signal_connect (priv->supplicant.iface,
+											  "state",
+											  G_CALLBACK (supplicant_iface_state_cb),
+											  self);
+
+	/* Hook up error signal handler to capture association errors */
+	priv->supplicant.iface_error_id = g_signal_connect (priv->supplicant.iface,
+											  "connection-error",
+											  G_CALLBACK (supplicant_iface_connection_error_cb),
+											  self);
+
+	priv->supplicant.iface_con_state_id = g_signal_connect (priv->supplicant.iface,
+												 "connection-state",
+												 G_CALLBACK (supplicant_iface_connection_state_cb),
+												 self);
+
+	/* Listen for supplicant manager state changes */
+	priv->supplicant.mgr_state_id = g_signal_connect (priv->supplicant.mgr,
+											"state",
+											G_CALLBACK (supplicant_mgr_state_cb),
+											self);
+
+	/* Set up a timeout on the connection attempt to fail it after 25 seconds */
+	priv->supplicant.con_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
+
+	return TRUE;
+}
+
+static NMActStageReturn
+nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
+{
+	NMConnection *connection;
+	NMSetting8021x *security;
+	NMSettingConnection *s_connection;
+	const char *setting_name;
+	const char *iface;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+
+	connection = nm_act_request_get_connection (nm_device_get_act_request (NM_DEVICE (self)));
+	security = NM_SETTING_802_1X (nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X));
+	if (!security) {
+		nm_log_err (LOGD_DEVICE, "Invalid or missing 802.1X security");
+		*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
+		return ret;
+	}
+
+	iface = nm_device_get_iface (NM_DEVICE (self));
+	s_connection = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+
+	/* If we need secrets, get them */
+	setting_name = nm_connection_need_secrets (connection, NULL);
+	if (setting_name) {
+		NMActRequest *req = nm_device_get_act_request (NM_DEVICE (self));
+
+		nm_log_info (LOGD_DEVICE | LOGD_ETHER,
+		             "Activation (%s/wired): connection '%s' has security, but secrets are required.",
+				     iface, nm_setting_connection_get_id (s_connection));
+
+		ret = handle_auth_or_fail (self, req, FALSE);
+		if (ret != NM_ACT_STAGE_RETURN_POSTPONE)
+			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+	} else {
+		nm_log_info (LOGD_DEVICE | LOGD_ETHER,
+		             "Activation (%s/wired): connection '%s' requires no security. No secrets needed.",
+				     iface, nm_setting_connection_get_id (s_connection));
+
+		if (supplicant_interface_init (self))
+			ret = NM_ACT_STAGE_RETURN_POSTPONE;
+		else
+			*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
+	}
+
+	return ret;
+}
+
+/*****************************************************************************/
+/* PPPoE */
+
+static void
+ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+
+	switch (status) {
+	case NM_PPP_STATUS_DISCONNECT:
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_DISCONNECT);
+		break;
+	case NM_PPP_STATUS_DEAD:
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+ppp_ip4_config (NMPPPManager *ppp_manager,
+			 const char *iface,
+			 NMIP4Config *config,
+			 gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+
+	/* Ignore PPP IP4 events that come in after initial configuration */
+	if (nm_device_get_state (device) != NM_DEVICE_STATE_IP_CONFIG)
+		return;
+
+	nm_device_set_ip_iface (device, iface);
+	NM_DEVICE_ETHERNET_GET_PRIVATE (device)->pending_ip4_config = g_object_ref (config);
+	nm_device_activate_schedule_stage4_ip4_config_get (device);
+}
+
+static NMActStageReturn
+pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reason)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	NMConnection *connection;
+	NMSettingPPPOE *s_pppoe;
+	NMActRequest *req;
+	GError *err = NULL;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	g_assert (req);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (req);
+
+	s_pppoe = (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
+	g_assert (s_pppoe);
+
+	priv->ppp_manager = nm_ppp_manager_new (nm_device_get_iface (NM_DEVICE (self)));
+	if (nm_ppp_manager_start (priv->ppp_manager, req, nm_setting_pppoe_get_username (s_pppoe), 30, &err)) {
+		g_signal_connect (priv->ppp_manager, "state-changed",
+					   G_CALLBACK (ppp_state_changed),
+					   self);
+		g_signal_connect (priv->ppp_manager, "ip4-config",
+					   G_CALLBACK (ppp_ip4_config),
+					   self);
+		ret = NM_ACT_STAGE_RETURN_POSTPONE;
+	} else {
+		nm_log_warn (LOGD_DEVICE, "(%s): PPPoE failed to start: %s",
+		             nm_device_get_iface (NM_DEVICE (self)), err->message);
+		g_error_free (err);
+
+		g_object_unref (priv->ppp_manager);
+		priv->ppp_manager = NULL;
+
+		*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
+	}
+
+	return ret;
+}
+
+static NMActStageReturn
+real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+{
+	NMSettingConnection *s_con;
+	const char *connection_type;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	s_con = NM_SETTING_CONNECTION (device_get_setting (device, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	/* 802.1x has to run before any IP configuration since the 802.1x auth
+	 * process opens the port up for normal traffic.
+	 */
+	connection_type = nm_setting_connection_get_connection_type (s_con);
+	if (!strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)) {
+		NMSetting8021x *security;
+
+		security = (NMSetting8021x *) device_get_setting (device, NM_TYPE_SETTING_802_1X);
+		if (security)
+			ret = nm_8021x_stage2_config (NM_DEVICE_ETHERNET (device), reason);
+	}
+
+	return ret;
+}
+
+static NMActStageReturn
+real_act_stage3_ip4_config_start (NMDevice *device, NMDeviceStateReason *reason)
+{
+	NMSettingConnection *s_con;
+	const char *connection_type;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	s_con = NM_SETTING_CONNECTION (device_get_setting (device, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	connection_type = nm_setting_connection_get_connection_type (s_con);
+	if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME))
+		return pppoe_stage3_ip4_config_start (NM_DEVICE_ETHERNET (device), reason);
+
+	return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage3_ip4_config_start (device, reason);
+}
+
+static NMActStageReturn
+real_act_stage4_get_ip4_config (NMDevice *device,
+                                NMIP4Config **config,
+                                NMDeviceStateReason *reason)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	NMActStageReturn ret;
+
+	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	if (!priv->ppp_manager) {
+		/* Regular ethernet connection. */
+
+		/* Chain up to parent */
+		ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage4_get_ip4_config (device, config, reason);
+
+		if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
+			NMConnection *connection;
+			NMSettingWired *s_wired;
+			guint32 mtu;
+
+			connection = nm_act_request_get_connection (nm_device_get_act_request (device));
+			g_assert (connection);
+			s_wired = NM_SETTING_WIRED (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED));
+			g_assert (s_wired);
+
+			/* MTU override */
+			mtu = nm_setting_wired_get_mtu (s_wired);
+			if (mtu)
+				nm_ip4_config_set_mtu (*config, mtu);
+		}
+	} else {
+		NMConnection *connection;
+		NMSettingIP4Config *s_ip4;
+
+		/* PPPoE */
+		*config = priv->pending_ip4_config;
+		priv->pending_ip4_config = NULL;
+
+		/* Merge user-defined overrides into the IP4Config to be applied */
+		connection = nm_act_request_get_connection (nm_device_get_act_request (device));
+		g_assert (connection);
+		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+		nm_utils_merge_ip4_config (*config, s_ip4);
+
+		ret = NM_ACT_STAGE_RETURN_SUCCESS;
+	}
+
+	return ret;
+}
+
+static void
+real_deactivate_quickly (NMDevice *device)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
+
+	nm_device_set_ip_iface (device, NULL);
+
+	if (priv->pending_ip4_config) {
+		g_object_unref (priv->pending_ip4_config);
+		priv->pending_ip4_config = NULL;
+	}
+
+	if (priv->ppp_manager) {
+		g_object_unref (priv->ppp_manager);
+		priv->ppp_manager = NULL;
+	}
+
+	supplicant_interface_release (NM_DEVICE_ETHERNET (device));
+}
+
+static gboolean
+real_check_connection_compatible (NMDevice *device,
+                                  NMConnection *connection,
+                                  GError **error)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	const char *connection_type;
+	gboolean is_pppoe = FALSE;
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	connection_type = nm_setting_connection_get_connection_type (s_con);
+	if (   strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)
+	    && strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) {
+		g_set_error (error,
+		             NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_NOT_WIRED,
+		             "The connection was not a wired or PPPoE connection.");
+		return FALSE;
+	}
+
+	if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME))
+		is_pppoe = TRUE;
+
+	s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+	/* Wired setting is optional for PPPoE */
+	if (!is_pppoe && !s_wired) {
+		g_set_error (error,
+		             NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INVALID,
+		             "The connection was not a valid wired connection.");
+		return FALSE;
+	}
+
+	if (s_wired) {
+		const GByteArray *mac;
+
+		mac = nm_setting_wired_get_mac_address (s_wired);
+		if (mac && memcmp (mac->data, &(priv->hw_addr.ether_addr_octet), ETH_ALEN)) {
+			g_set_error (error,
+			             NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE,
+			             "The connection's MAC address did not match this device.");
+			return FALSE;
+		}
+	}
+
+	// FIXME: check bitrate against device capabilities
+
+	return TRUE;
+}
+
+static gboolean
+spec_match_list (NMDevice *device, const GSList *specs)
+{
+	struct ether_addr ether;
+	char *hwaddr;
+	gboolean matched;
+
+	nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (device), &ether);
+	hwaddr = nm_ether_ntop (&ether);
+	matched = nm_match_spec_hwaddr (specs, hwaddr);
+	g_free (hwaddr);
+
+	return matched;
+}
+
+static gboolean
+wired_match_config (NMDevice *self, NMConnection *connection)
+{
+	NMSettingWired *s_wired;
+	struct ether_addr ether;
+	const GByteArray *s_ether;
+
+	s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+	if (!s_wired)
+		return FALSE;
+
+	/* MAC address check */
+	s_ether = nm_setting_wired_get_mac_address (s_wired);
+	if (s_ether) {
+		nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (self), &ether);
+
+		if (memcmp (s_ether->data, ether.ether_addr_octet, ETH_ALEN))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+typedef struct {
+	int ifindex;
+	NMIP4Address *addr;
+	gboolean found;
+} AddrData;
+
+static void
+check_one_address (struct nl_object *object, void *user_data)
+{
+	AddrData *data = user_data;
+	struct rtnl_addr *addr = (struct rtnl_addr *) object;
+	struct nl_addr *local;
+	struct in_addr tmp;
+
+	if (rtnl_addr_get_ifindex (addr) != data->ifindex)
+		return;
+	if (rtnl_addr_get_family (addr) != AF_INET)
+		return;
+
+	if (nm_ip4_address_get_prefix (data->addr) != rtnl_addr_get_prefixlen (addr))
+		return;
+
+	local = rtnl_addr_get_local (addr);
+	if (nl_addr_get_family (local) != AF_INET)
+		return;
+	if (nl_addr_get_len (local) != sizeof (struct in_addr))
+		return;
+	if (!nl_addr_get_binary_addr (local))
+		return;
+
+	memcpy (&tmp, nl_addr_get_binary_addr (local), nl_addr_get_len (local));
+	if (tmp.s_addr != nm_ip4_address_get_address (data->addr))
+		return;
+
+	/* Yay, found it */
+	data->found = TRUE;
+}
+
+static gboolean
+ip4_match_config (NMDevice *self, NMConnection *connection)
+{
+	NMSettingIP4Config *s_ip4;
+	NMSettingConnection *s_con;
+	struct nl_handle *nlh = NULL;
+	struct nl_cache *addr_cache = NULL;
+	int i, num;
+	GSList *leases, *iter;
+	NMDHCPManager *dhcp_mgr;
+	const char *method;
+	int ifindex;
+	AddrData check_data;
+
+	ifindex = nm_device_get_ifindex (self);
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
+	g_assert (nm_setting_connection_get_uuid (s_con));
+
+	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	if (!s_ip4)
+		return FALSE;
+
+	/* Read all the device's IP addresses */
+	nlh = nm_netlink_get_default_handle ();
+	if (!nlh)
+		return FALSE;
+
+	addr_cache = rtnl_addr_alloc_cache (nlh);
+	if (!addr_cache)
+		return FALSE;
+	nl_cache_mngt_provide (addr_cache);
+
+	/* Get any saved leases that apply to this connection */
+	dhcp_mgr = nm_dhcp_manager_get ();
+	leases = nm_dhcp_manager_get_lease_config (dhcp_mgr,
+	                                           nm_device_get_iface (self),
+	                                           nm_setting_connection_get_uuid (s_con));
+	g_object_unref (dhcp_mgr);
+
+	method = nm_setting_ip4_config_get_method (s_ip4);
+	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+		gboolean found = FALSE;
+
+		/* Find at least one lease's address on the device */
+		for (iter = leases; iter; iter = g_slist_next (iter)) {
+			NMIP4Config *addr = iter->data;
+
+			memset (&check_data, 0, sizeof (check_data));
+			check_data.ifindex = ifindex;
+			check_data.found = FALSE;
+			check_data.addr = nm_ip4_config_get_address (addr, 0);
+
+			nl_cache_foreach (addr_cache, check_one_address, &check_data);
+			if (check_data.found) {
+				found = TRUE; /* Yay, device has same address as a lease */
+				break;
+			}
+		}
+		g_slist_foreach (leases, (GFunc) g_object_unref, NULL);
+		g_slist_free (leases);
+		return found;
+	} else {
+		/* Maybe the connection used to be DHCP and there are stale leases; ignore them */
+		g_slist_foreach (leases, (GFunc) g_object_unref, NULL);
+		g_slist_free (leases);
+	}
+
+	/* 'shared' and 'link-local' aren't supported methods because 'shared'
+	 * requires too much iptables and dnsmasq state to be reclaimed, and
+	 * avahi-autoipd isn't smart enough to allow the link-local address to be
+	 * determined at any point other than when it was first assigned.
+	 */
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
+		return FALSE;
+
+	/* Everything below for static addressing */
+
+	/* Find all IP4 addresses of this connection in the device's address list */
+	num = nm_setting_ip4_config_get_num_addresses (s_ip4);
+	for (i = 0; i < num; i++) {
+		memset (&check_data, 0, sizeof (check_data));
+		check_data.ifindex = ifindex;
+		check_data.found = FALSE;
+		check_data.addr = nm_setting_ip4_config_get_address (s_ip4, i);
+
+		nl_cache_foreach (addr_cache, check_one_address, &check_data);
+		if (!check_data.found)
+			return FALSE;
+	}
+
+	/* Success; all the connection's static IP addresses are assigned to the device */
+	return TRUE;
+}
+
+static NMConnection *
+connection_match_config (NMDevice *self, const GSList *connections)
+{
+	GSList *iter;
+	NMSettingConnection *s_con;
+
+	for (iter = (GSList *) connections; iter; iter = g_slist_next (iter)) {
+		NMConnection *candidate = NM_CONNECTION (iter->data);
+
+		s_con = (NMSettingConnection *) nm_connection_get_setting (candidate, NM_TYPE_SETTING_CONNECTION);
+		g_assert (s_con);
+		if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRED_SETTING_NAME))
+			continue;
+
+		/* Can't assume 802.1x or PPPoE connections; they have too much state
+		 * that's impossible to get on-the-fly from PPPoE or the supplicant.
+		 */
+		if (   nm_connection_get_setting (candidate, NM_TYPE_SETTING_802_1X)
+		    || nm_connection_get_setting (candidate, NM_TYPE_SETTING_PPPOE))
+			continue;
+
+		if (!wired_match_config (self, candidate))
+			continue;
+
+		if (!ip4_match_config (self, candidate))
+			continue;
+
+		return candidate;
+	}
+
+	return NULL;
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	if (priv->disposed) {
+		G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object);
+		return;
+	}
+
+	priv->disposed = TRUE;
+
+	/* Clean up all pending supplicant tasks */
+	while (priv->supplicant.iface_tasks)
+		finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE);
+	while (priv->supplicant.mgr_tasks)
+		finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.mgr_tasks->data, TRUE);
+
+	if (priv->link_connected_id) {
+		g_signal_handler_disconnect (priv->monitor, priv->link_connected_id);
+		priv->link_connected_id = 0;
+	}
+	if (priv->link_disconnected_id) {
+		g_signal_handler_disconnect (priv->monitor, priv->link_disconnected_id);
+		priv->link_disconnected_id = 0;
+	}
+
+	carrier_action_defer_clear (self);
+
+	if (priv->monitor) {
+		g_object_unref (priv->monitor);
+		priv->monitor = NULL;
+	}
+
+	G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	struct ether_addr hw_addr;
+
+	switch (prop_id) {
+	case PROP_HW_ADDRESS:
+		nm_device_ethernet_get_address (self, &hw_addr);
+		g_value_take_string (value, nm_ether_ntop (&hw_addr));
+		break;
+	case PROP_SPEED:
+		g_value_set_uint (value, nm_device_ethernet_get_speed (self));
+		break;
+	case PROP_CARRIER:
+		g_value_set_boolean (value, priv->carrier);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMDeviceEthernetPrivate));
+
+	/* virtual methods */
+	object_class->constructor = constructor;
+	object_class->dispose = dispose;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+
+	parent_class->get_generic_capabilities = real_get_generic_capabilities;
+	parent_class->hw_is_up = real_hw_is_up;
+	parent_class->hw_bring_up = real_hw_bring_up;
+	parent_class->hw_take_down = real_hw_take_down;
+	parent_class->is_up = real_is_up;
+	parent_class->bring_up = real_bring_up;
+	parent_class->take_down = real_take_down;
+	parent_class->can_interrupt_activation = real_can_interrupt_activation;
+	parent_class->update_hw_address = real_update_hw_address;
+	parent_class->get_best_auto_connection = real_get_best_auto_connection;
+	parent_class->is_available = real_is_available;
+	parent_class->connection_secrets_updated = real_connection_secrets_updated;
+	parent_class->check_connection_compatible = real_check_connection_compatible;
+
+	parent_class->act_stage2_config = real_act_stage2_config;
+	parent_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start;
+	parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
+	parent_class->deactivate_quickly = real_deactivate_quickly;
+	parent_class->spec_match_list = spec_match_list;
+	parent_class->connection_match_config = connection_match_config;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_HW_ADDRESS,
+		 g_param_spec_string (NM_DEVICE_ETHERNET_HW_ADDRESS,
+							  "MAC Address",
+							  "Hardware MAC address",
+							  NULL,
+							  G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_SPEED,
+		 g_param_spec_uint (NM_DEVICE_ETHERNET_SPEED,
+						   "Speed",
+						   "Speed",
+						   0, G_MAXUINT32, 0,
+						   G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_CARRIER,
+		 g_param_spec_boolean (NM_DEVICE_ETHERNET_CARRIER,
+							   "Carrier",
+							   "Carrier",
+							   FALSE,
+							   G_PARAM_READABLE));
+
+	/* Signals */
+	signals[PROPERTIES_CHANGED] = 
+		nm_properties_changed_signal_new (object_class,
+								    G_STRUCT_OFFSET (NMDeviceEthernetClass, properties_changed));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass),
+									 &dbus_glib_nm_device_ethernet_object_info);
+
+	dbus_g_error_domain_register (NM_ETHERNET_ERROR, NULL, NM_TYPE_ETHERNET_ERROR);
+}
+
+
+/**************************************/
+/*    Ethtool capability detection    */
+/**************************************/
+
+static gboolean
+supports_ethtool_carrier_detect (NMDeviceEthernet *self)
+{
+	int fd;
+	struct ifreq ifr;
+	gboolean supports_ethtool = FALSE;
+	struct ethtool_cmd edata;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_HW, "couldn't open control socket.");
+		return FALSE;
+	}
+
+	memset (&ifr, 0, sizeof (struct ifreq));
+	strncpy (ifr.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ);
+
+	edata.cmd = ETHTOOL_GLINK;
+	ifr.ifr_data = (char *) &edata;
+
+	errno = 0;
+	if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) {
+		nm_log_dbg (LOGD_HW | LOGD_ETHER, "SIOCETHTOOL failed: %d", errno);
+		goto out;
+	}
+
+	supports_ethtool = TRUE;
+
+out:
+	close (fd);
+	nm_log_dbg (LOGD_HW | LOGD_ETHER, "ethtool %s supported",
+	            supports_ethtool ? "is" : "not");
+	return supports_ethtool;
+}
+
+
+/**************************************/
+/*    MII capability detection        */
+/**************************************/
+#define _LINUX_IF_H
+#include <linux/mii.h>
+#undef _LINUX_IF_H
+
+static int
+mdio_read (NMDeviceEthernet *self, int fd, struct ifreq *ifr, int location)
+{
+	struct mii_ioctl_data *mii;
+	int val = -1;
+
+	g_return_val_if_fail (fd >= 0, -1);
+	g_return_val_if_fail (ifr != NULL, -1);
+
+	mii = (struct mii_ioctl_data *) &ifr->ifr_ifru;
+	mii->reg_num = location;
+
+	errno = 0;
+	if (ioctl (fd, SIOCGMIIREG, ifr) == 0) {
+		nm_log_dbg (LOGD_HW | LOGD_ETHER, "SIOCGMIIREG result 0x%X", mii->val_out);
+		val = mii->val_out;
+	} else {
+		nm_log_dbg (LOGD_HW | LOGD_ETHER, "SIOCGMIIREG failed: %d", errno);
+	}
+
+	return val;
+}
+
+static gboolean
+supports_mii_carrier_detect (NMDeviceEthernet *self)
+{
+	int fd, bmsr;
+	struct ifreq ifr;
+	gboolean supports_mii = FALSE;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_HW, "couldn't open control socket.");
+		return 0;
+	}
+
+	memset (&ifr, 0, sizeof (struct ifreq));
+	strncpy (ifr.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ);
+
+	errno = 0;
+	if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) {
+		nm_log_dbg (LOGD_HW | LOGD_ETHER, "SIOCGMIIPHY failed: %d", errno);
+		goto out;
+	}
+
+	/* If we can read the BMSR register, we assume that the card supports MII link detection */
+	bmsr = mdio_read (self, fd, &ifr, MII_BMSR);
+	supports_mii = (bmsr != -1) ? TRUE : FALSE;
+	nm_log_dbg (LOGD_HW | LOGD_ETHER, "MII %s supported",
+	            supports_mii ? "is" : "not");
+
+out:
+	close (fd);
+	return supports_mii;	
+}
diff --git a/src/nm-device-ethernet.h b/src/nm-device-ethernet.h
new file mode 100644
index 00000000..7bb3db00
--- /dev/null
+++ b/src/nm-device-ethernet.h
@@ -0,0 +1,67 @@
+/* -*- 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_DEVICE_ETHERNET_H
+#define NM_DEVICE_ETHERNET_H
+
+#include <glib-object.h>
+#include <net/ethernet.h>
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_ETHERNET			(nm_device_ethernet_get_type ())
+#define NM_DEVICE_ETHERNET(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet))
+#define NM_DEVICE_ETHERNET_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
+#define NM_IS_DEVICE_ETHERNET(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ETHERNET))
+#define NM_IS_DEVICE_ETHERNET_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_ETHERNET))
+#define NM_DEVICE_ETHERNET_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
+
+#define NM_DEVICE_ETHERNET_HW_ADDRESS "hw-address"
+#define NM_DEVICE_ETHERNET_SPEED "speed"
+#define NM_DEVICE_ETHERNET_CARRIER "carrier"
+
+typedef struct {
+	NMDevice parent;
+} NMDeviceEthernet;
+
+typedef struct {
+	NMDeviceClass parent;
+
+	/* Signals */
+	void (*properties_changed) (NMDeviceEthernet *device, GHashTable *properties);
+} NMDeviceEthernetClass;
+
+
+GType nm_device_ethernet_get_type (void);
+
+
+NMDevice *nm_device_ethernet_new (const char *udi,
+                                  const char *iface,
+                                  const char *driver);
+
+void nm_device_ethernet_get_address (NMDeviceEthernet *dev,
+                                     struct ether_addr *addr);
+
+G_END_DECLS
+
+#endif	/* NM_DEVICE_ETHERNET_H */
diff --git a/src/nm-device-gsm.c b/src/nm-device-gsm.c
new file mode 100644
index 00000000..2a98a41f
--- /dev/null
+++ b/src/nm-device-gsm.c
@@ -0,0 +1,75 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-modem-gsm.h"
+#include "nm-device-interface.h"
+#include "nm-device-gsm.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-rfkill.h"
+
+#include "nm-device-gsm-glue.h"
+
+G_DEFINE_TYPE (NMDeviceGsm, nm_device_gsm, NM_TYPE_DEVICE_MODEM)
+
+enum {
+	PROPERTIES_CHANGED,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+NMDevice *
+nm_device_gsm_new (NMModemGsm *modem, const char *driver)
+{
+	g_return_val_if_fail (modem != NULL, NULL);
+	g_return_val_if_fail (NM_IS_MODEM_GSM (modem), NULL);
+	g_return_val_if_fail (driver != NULL, NULL);
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_GSM,
+	                                  NM_DEVICE_INTERFACE_UDI, nm_modem_get_path (NM_MODEM (modem)),
+	                                  NM_DEVICE_INTERFACE_IFACE, nm_modem_get_iface (NM_MODEM (modem)),
+	                                  NM_DEVICE_INTERFACE_DRIVER, driver,
+	                                  NM_DEVICE_INTERFACE_TYPE_DESC, "GSM",
+	                                  NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_GSM,
+	                                  NM_DEVICE_INTERFACE_RFKILL_TYPE, RFKILL_TYPE_WWAN,
+	                                  NM_DEVICE_MODEM_MODEM, modem,
+	                                  NULL);
+}
+
+static void
+nm_device_gsm_init (NMDeviceGsm *self)
+{
+}
+
+static void
+nm_device_gsm_class_init (NMDeviceGsmClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	/* Signals */
+	signals[PROPERTIES_CHANGED] = 
+		nm_properties_changed_signal_new (object_class,
+		                                  G_STRUCT_OFFSET (NMDeviceGsmClass, properties_changed));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass),
+	                                 &dbus_glib_nm_device_gsm_object_info);
+}
+
diff --git a/src/nm-device-gsm.h b/src/nm-device-gsm.h
new file mode 100644
index 00000000..9b403c63
--- /dev/null
+++ b/src/nm-device-gsm.h
@@ -0,0 +1,55 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_GSM_H
+#define NM_DEVICE_GSM_H
+
+#include "nm-device-modem.h"
+#include "nm-modem-gsm.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_GSM            (nm_device_gsm_get_type ())
+#define NM_DEVICE_GSM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_GSM, NMDeviceGsm))
+#define NM_DEVICE_GSM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_GSM, NMDeviceGsmClass))
+#define NM_IS_DEVICE_GSM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_GSM))
+#define NM_IS_DEVICE_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_GSM))
+#define NM_DEVICE_GSM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_GSM, NMDeviceGsmClass))
+
+typedef struct {
+	NMDeviceModem parent;
+} NMDeviceGsm;
+
+typedef struct {
+	NMDeviceModemClass parent;
+
+	/* Signals */
+	void (*signal_quality) (NMDeviceGsm *self, guint32 quality);
+
+	void (*properties_changed) (NMDeviceGsm *self, GHashTable *properties);
+} NMDeviceGsmClass;
+
+GType nm_device_gsm_get_type (void);
+
+NMDevice *nm_device_gsm_new (NMModemGsm *modem, const char *driver);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_GSM_H */
diff --git a/src/nm-device-interface.c b/src/nm-device-interface.c
new file mode 100644
index 00000000..85f58d55
--- /dev/null
+++ b/src/nm-device-interface.c
@@ -0,0 +1,402 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2010 Red Hat, Inc.
+ */
+
+#include "nm-marshal.h"
+#include "nm-setting-connection.h"
+#include "nm-device-interface.h"
+#include "nm-logging.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-rfkill.h"
+
+static gboolean impl_device_disconnect (NMDeviceInterface *device,
+                                        GError **error);
+
+#include "nm-device-interface-glue.h"
+
+GQuark
+nm_device_interface_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-device-interface-error");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_device_interface_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Connection is already activating. */
+			ENUM_ENTRY (NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING, "ConnectionActivating"),
+			/* Connection is invalid for this device. */
+			ENUM_ENTRY (NM_DEVICE_INTERFACE_ERROR_CONNECTION_INVALID, "ConnectionInvalid"),
+			/* Operation could not be performed because the device is not active. */
+			ENUM_ENTRY (NM_DEVICE_INTERFACE_ERROR_NOT_ACTIVE, "NotActive"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMDeviceInterfaceError", values);
+	}
+	return etype;
+}
+
+
+static void
+nm_device_interface_init (gpointer g_iface)
+{
+	GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+	static gboolean initialized = FALSE;
+
+	if (initialized)
+		return;
+
+	/* Properties */
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_string (NM_DEVICE_INTERFACE_UDI,
+							  "UDI",
+							  "Unique Device Identifier",
+							  NULL,
+							  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_string (NM_DEVICE_INTERFACE_IFACE,
+							  "Interface",
+							  "Interface",
+							  NULL,
+							  G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_string (NM_DEVICE_INTERFACE_DRIVER,
+							  "Driver",
+							  "Driver",
+							  NULL,
+							  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_uint (NM_DEVICE_INTERFACE_CAPABILITIES,
+							"Capabilities",
+							"Capabilities",
+							0, G_MAXUINT32, NM_DEVICE_CAP_NONE,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_uint (NM_DEVICE_INTERFACE_IP4_ADDRESS,
+							"IP4 address",
+							"IP4 address",
+							0, G_MAXUINT32, 0, /* FIXME */
+							G_PARAM_READWRITE));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_boxed (NM_DEVICE_INTERFACE_IP4_CONFIG,
+							  "IP4 Config",
+							  "IP4 Config",
+							  DBUS_TYPE_G_OBJECT_PATH,
+							  G_PARAM_READWRITE));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_boxed (NM_DEVICE_INTERFACE_DHCP4_CONFIG,
+							  "DHCP4 Config",
+							  "DHCP4 Config",
+							  DBUS_TYPE_G_OBJECT_PATH,
+							  G_PARAM_READWRITE));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_boxed (NM_DEVICE_INTERFACE_IP6_CONFIG,
+							  "IP6 Config",
+							  "IP6 Config",
+							  DBUS_TYPE_G_OBJECT_PATH,
+							  G_PARAM_READWRITE));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_boxed (NM_DEVICE_INTERFACE_DHCP6_CONFIG,
+							  "DHCP6 Config",
+							  "DHCP6 Config",
+							  DBUS_TYPE_G_OBJECT_PATH,
+							  G_PARAM_READWRITE));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_uint (NM_DEVICE_INTERFACE_STATE,
+							"State",
+							"State",
+							0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN,
+							G_PARAM_READABLE));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_uint (NM_DEVICE_INTERFACE_DEVICE_TYPE,
+							"DeviceType",
+							"DeviceType",
+							0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	g_object_interface_install_property
+		(g_iface, g_param_spec_boolean (NM_DEVICE_INTERFACE_MANAGED,
+	                                   "Managed",
+	                                   "Managed",
+	                                   FALSE,
+	                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_string (NM_DEVICE_INTERFACE_TYPE_DESC,
+							  "Type Description",
+							  "Device type description",
+							  NULL,
+							  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	g_object_interface_install_property
+		(g_iface, g_param_spec_uint (NM_DEVICE_INTERFACE_RFKILL_TYPE,
+	                                 "Rfkill Type",
+	                                 "Type of rfkill switch (if any) supported by this device",
+	                                 RFKILL_TYPE_WLAN,
+	                                 RFKILL_TYPE_MAX,
+	                                 RFKILL_TYPE_UNKNOWN,
+	                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_int (NM_DEVICE_INTERFACE_IFINDEX,
+							"Ifindex",
+							"Ifindex",
+							0, G_MAXINT, 0,
+							G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	/* Signals */
+	g_signal_new ("state-changed",
+				  iface_type,
+				  G_SIGNAL_RUN_FIRST,
+				  G_STRUCT_OFFSET (NMDeviceInterface, state_changed),
+				  NULL, NULL,
+				  _nm_marshal_VOID__UINT_UINT_UINT,
+				  G_TYPE_NONE, 3,
+				  G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+
+	dbus_g_object_type_install_info (iface_type,
+									 &dbus_glib_nm_device_interface_object_info);
+
+	dbus_g_error_domain_register (NM_DEVICE_INTERFACE_ERROR,
+	                              NULL,
+	                              NM_TYPE_DEVICE_INTERFACE_ERROR);
+
+	initialized = TRUE;
+}
+
+
+GType
+nm_device_interface_get_type (void)
+{
+	static GType device_interface_type = 0;
+
+	if (!device_interface_type) {
+		const GTypeInfo device_interface_info = {
+			sizeof (NMDeviceInterface), /* class_size */
+			nm_device_interface_init,   /* base_init */
+			NULL,		/* base_finalize */
+			NULL,
+			NULL,		/* class_finalize */
+			NULL,		/* class_data */
+			0,
+			0,              /* n_preallocs */
+			NULL
+		};
+
+		device_interface_type = g_type_register_static (G_TYPE_INTERFACE,
+														"NMDeviceInterface",
+														&device_interface_info, 0);
+
+		g_type_interface_add_prerequisite (device_interface_type, G_TYPE_OBJECT);
+	}
+
+	return device_interface_type;
+}
+
+/* FIXME: This should be public and nm_device_get_iface() should be removed. */
+static char *
+nm_device_interface_get_iface (NMDeviceInterface *device)
+{
+	char *iface = NULL;
+
+	g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), NULL);
+
+	g_object_get (device, NM_DEVICE_INTERFACE_IFACE, &iface, NULL);
+
+	return iface;
+}
+
+gboolean
+nm_device_interface_check_connection_compatible (NMDeviceInterface *device,
+                                                 NMConnection *connection,
+                                                 GError **error)
+{
+	g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->check_connection_compatible)
+		return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->check_connection_compatible (device, connection, error);
+	return TRUE;
+}
+
+gboolean
+nm_device_interface_activate (NMDeviceInterface *device,
+                              NMActRequest *req,
+                              GError **error)
+{
+	gboolean success;
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	char *iface;
+
+	g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	iface = nm_device_interface_get_iface (device);
+	nm_log_info (LOGD_DEVICE, "Activation (%s) starting connection '%s'", iface,
+			     nm_setting_connection_get_id (s_con));
+	g_free (iface);
+
+	success = NM_DEVICE_INTERFACE_GET_INTERFACE (device)->activate (device, req, error);
+	if (!success)
+		g_assert (*error);
+
+	return success;
+}
+
+gboolean
+nm_device_interface_disconnect (NMDeviceInterface *device,
+                                GError **error)
+{
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE);
+
+	switch (nm_device_interface_get_state (device)) {
+	case NM_DEVICE_STATE_UNKNOWN:
+	case NM_DEVICE_STATE_UNMANAGED:
+	case NM_DEVICE_STATE_UNAVAILABLE:
+	case NM_DEVICE_STATE_DISCONNECTED:
+		g_set_error_literal (error,
+		                     NM_DEVICE_INTERFACE_ERROR,
+		                     NM_DEVICE_INTERFACE_ERROR_NOT_ACTIVE,
+		                     "Cannot disconnect an inactive device.");
+		break;
+	default:
+		success = NM_DEVICE_INTERFACE_GET_INTERFACE (device)->disconnect (device, error);
+		break;
+	}
+
+	return success;
+}
+
+static gboolean
+impl_device_disconnect (NMDeviceInterface *device,
+                        GError **error)
+{
+	return nm_device_interface_disconnect (device, error);
+}
+
+void
+nm_device_interface_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason)
+{
+	g_return_if_fail (NM_IS_DEVICE_INTERFACE (device));
+
+	NM_DEVICE_INTERFACE_GET_INTERFACE (device)->deactivate (device, reason);
+}
+
+NMDeviceState
+nm_device_interface_get_state (NMDeviceInterface *device)
+{
+	NMDeviceState state;
+
+	g_object_get (G_OBJECT (device), "state", &state, NULL);
+	return state;
+}
+
+gboolean
+nm_device_interface_spec_match_list (NMDeviceInterface *device,
+                                     const GSList *specs)
+{
+	g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE);
+
+	if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list)
+		return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->spec_match_list (device, specs);
+	return FALSE;
+}
+
+NMConnection *
+nm_device_interface_connection_match_config (NMDeviceInterface *device,
+                                             const GSList *connections)
+{
+	g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), NULL);
+
+	if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config)
+		return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config (device, connections);
+	return NULL;
+}
+
+gboolean
+nm_device_interface_can_assume_connection (NMDeviceInterface *device)
+{
+	g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE);
+
+	return !!NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config;
+}
+
+void
+nm_device_interface_set_enabled (NMDeviceInterface *device, gboolean enabled)
+{
+	g_return_if_fail (NM_IS_DEVICE_INTERFACE (device));
+
+	if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->set_enabled)
+		NM_DEVICE_INTERFACE_GET_INTERFACE (device)->set_enabled (device, enabled);
+}
+
+gboolean
+nm_device_interface_get_enabled (NMDeviceInterface *device)
+{
+	g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE);
+
+	if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->get_enabled)
+		return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->get_enabled (device);
+	return TRUE;
+}
+
diff --git a/src/nm-device-interface.h b/src/nm-device-interface.h
new file mode 100644
index 00000000..ec27f6e8
--- /dev/null
+++ b/src/nm-device-interface.h
@@ -0,0 +1,148 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_INTERFACE_H
+#define NM_DEVICE_INTERFACE_H
+
+#include <glib-object.h>
+#include "NetworkManager.h"
+#include "nm-connection.h"
+#include "nm-activation-request.h"
+
+#define NM_TYPE_DEVICE_INTERFACE      (nm_device_interface_get_type ())
+#define NM_DEVICE_INTERFACE(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_INTERFACE, NMDeviceInterface))
+#define NM_IS_DEVICE_INTERFACE(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_INTERFACE))
+#define NM_DEVICE_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_DEVICE_INTERFACE, NMDeviceInterface))
+
+#define IS_ACTIVATING_STATE(state) \
+	(state > NM_DEVICE_STATE_DISCONNECTED && state < NM_DEVICE_STATE_ACTIVATED)
+
+typedef enum
+{
+	NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING = 0,
+	NM_DEVICE_INTERFACE_ERROR_CONNECTION_INVALID,
+	NM_DEVICE_INTERFACE_ERROR_NOT_ACTIVE,
+} NMDeviceInterfaceError;
+
+#define NM_DEVICE_INTERFACE_ERROR (nm_device_interface_error_quark ())
+#define NM_TYPE_DEVICE_INTERFACE_ERROR (nm_device_interface_error_get_type ()) 
+
+#define NM_DEVICE_INTERFACE_UDI          "udi"
+#define NM_DEVICE_INTERFACE_IFACE        "interface"
+#define NM_DEVICE_INTERFACE_DRIVER       "driver"
+#define NM_DEVICE_INTERFACE_CAPABILITIES "capabilities"
+#define NM_DEVICE_INTERFACE_IP4_ADDRESS  "ip4-address"
+#define NM_DEVICE_INTERFACE_IP4_CONFIG   "ip4-config"
+#define NM_DEVICE_INTERFACE_DHCP4_CONFIG "dhcp4-config"
+#define NM_DEVICE_INTERFACE_IP6_CONFIG   "ip6-config"
+#define NM_DEVICE_INTERFACE_DHCP6_CONFIG "dhcp6-config"
+#define NM_DEVICE_INTERFACE_STATE        "state"
+#define NM_DEVICE_INTERFACE_DEVICE_TYPE  "device-type" /* ugh */
+#define NM_DEVICE_INTERFACE_MANAGED      "managed"
+#define NM_DEVICE_INTERFACE_TYPE_DESC    "type-desc"    /* Internal only */
+#define NM_DEVICE_INTERFACE_RFKILL_TYPE  "rfkill-type"  /* Internal only */
+#define NM_DEVICE_INTERFACE_IFINDEX      "ifindex"      /* Internal only */
+
+typedef enum {
+	NM_DEVICE_INTERFACE_PROP_FIRST = 0x1000,
+
+	NM_DEVICE_INTERFACE_PROP_UDI = NM_DEVICE_INTERFACE_PROP_FIRST,
+	NM_DEVICE_INTERFACE_PROP_IFACE,
+	NM_DEVICE_INTERFACE_PROP_DRIVER,
+	NM_DEVICE_INTERFACE_PROP_CAPABILITIES,
+	NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS,
+	NM_DEVICE_INTERFACE_PROP_IP4_CONFIG,
+	NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG,
+	NM_DEVICE_INTERFACE_PROP_IP6_CONFIG,
+	NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG,
+	NM_DEVICE_INTERFACE_PROP_STATE,
+	NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE,
+	NM_DEVICE_INTERFACE_PROP_MANAGED,
+	NM_DEVICE_INTERFACE_PROP_TYPE_DESC,
+	NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE,
+	NM_DEVICE_INTERFACE_PROP_IFINDEX,
+} NMDeviceInterfaceProp;
+
+
+typedef struct _NMDeviceInterface NMDeviceInterface;
+
+struct _NMDeviceInterface {
+	GTypeInterface g_iface;
+
+	/* Methods */
+	gboolean (*check_connection_compatible) (NMDeviceInterface *device,
+	                                         NMConnection *connection,
+	                                         GError **error);
+
+	gboolean (*activate) (NMDeviceInterface *device,
+	                      NMActRequest *req,
+	                      GError **error);
+
+	void (*deactivate) (NMDeviceInterface *device, NMDeviceStateReason reason);
+	gboolean (*disconnect) (NMDeviceInterface *device, GError **error);
+
+	gboolean (*spec_match_list) (NMDeviceInterface *device, const GSList *specs);
+
+	NMConnection * (*connection_match_config) (NMDeviceInterface *device, const GSList *specs);
+
+	void (*set_enabled) (NMDeviceInterface *device, gboolean enabled);
+
+	gboolean (*get_enabled) (NMDeviceInterface *device);
+
+	/* Signals */
+	void (*state_changed) (NMDeviceInterface *device,
+	                       NMDeviceState new_state,
+	                       NMDeviceState old_state,
+	                       NMDeviceStateReason reason);
+};
+
+GQuark nm_device_interface_error_quark (void);
+GType nm_device_interface_error_get_type (void);
+
+gboolean nm_device_interface_disconnect (NMDeviceInterface *device, GError **error);
+
+GType nm_device_interface_get_type (void);
+
+gboolean nm_device_interface_check_connection_compatible (NMDeviceInterface *device,
+                                                          NMConnection *connection,
+                                                          GError **error);
+
+gboolean nm_device_interface_activate (NMDeviceInterface *device,
+				       NMActRequest *req,
+				       GError **error);
+
+void nm_device_interface_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason);
+
+NMDeviceState nm_device_interface_get_state (NMDeviceInterface *device);
+
+gboolean nm_device_interface_spec_match_list (NMDeviceInterface *device,
+                                              const GSList *specs);
+
+NMConnection * nm_device_interface_connection_match_config (NMDeviceInterface *device,
+                                                            const GSList *connections);
+
+gboolean nm_device_interface_can_assume_connection (NMDeviceInterface *device);
+
+gboolean nm_device_interface_get_enabled (NMDeviceInterface *device);
+
+void nm_device_interface_set_enabled (NMDeviceInterface *device, gboolean enabled);
+
+#endif /* NM_DEVICE_INTERFACE_H */
diff --git a/src/nm-device-modem.c b/src/nm-device-modem.c
new file mode 100644
index 00000000..441f92ce
--- /dev/null
+++ b/src/nm-device-modem.c
@@ -0,0 +1,471 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <glib.h>
+
+#include "nm-device-modem.h"
+#include "nm-device-interface.h"
+#include "nm-modem.h"
+#include "nm-device-private.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-marshal.h"
+#include "nm-logging.h"
+
+static void device_interface_init (NMDeviceInterface *iface_class);
+
+G_DEFINE_TYPE_EXTENDED (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE, G_TYPE_FLAG_ABSTRACT,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE, device_interface_init))
+
+#define NM_DEVICE_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MODEM, NMDeviceModemPrivate))
+
+typedef struct {
+	NMModem *modem;
+} NMDeviceModemPrivate;
+
+enum {
+	PROP_0,
+	PROP_MODEM
+};
+
+enum {
+	PPP_STATS,
+	ENABLE_CHANGED,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void real_set_enabled (NMDeviceInterface *device, gboolean enabled);
+
+/*****************************************************************************/
+
+static void
+ppp_stats (NMModem *modem,
+		   guint32 in_bytes,
+		   guint32 out_bytes,
+		   gpointer user_data)
+{
+	g_signal_emit (G_OBJECT (user_data), signals[PPP_STATS], 0, in_bytes, out_bytes);
+}
+
+static void
+ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+
+	switch (nm_device_interface_get_state (NM_DEVICE_INTERFACE (device))) {
+	case NM_DEVICE_STATE_PREPARE:
+	case NM_DEVICE_STATE_CONFIG:
+	case NM_DEVICE_STATE_NEED_AUTH:
+	case NM_DEVICE_STATE_ACTIVATED:
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		break;
+	case NM_DEVICE_STATE_IP_CONFIG:
+		if (nm_device_ip_config_should_fail (device, FALSE)) {
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+modem_prepare_result (NMModem *modem,
+                      gboolean success,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceState state;
+
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device));
+	g_return_if_fail (state == NM_DEVICE_STATE_PREPARE);
+
+	if (success)
+		nm_device_activate_schedule_stage2_device_config (device);
+	else
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+}
+
+static void
+modem_need_auth (NMModem *modem,
+	             const char *setting_name,
+	             gboolean retry,
+	             RequestSecretsCaller caller,
+	             const char *hint1,
+	             const char *hint2,
+	             gpointer user_data)
+{
+	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
+	NMActRequest *req;
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	g_assert (req);
+
+	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
+	nm_act_request_get_secrets (req, setting_name, retry, caller, hint1, hint2);
+}
+
+static void
+modem_ip4_config_result (NMModem *self,
+                         const char *iface,
+                         NMIP4Config *config,
+                         GError *error,
+                         gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceState state;
+
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device));
+	g_return_if_fail (state == NM_DEVICE_STATE_IP_CONFIG);
+
+	if (error) {
+		nm_log_warn (LOGD_MB | LOGD_IP4, "retrieving IP4 configuration failed: (%d) %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+	} else {
+		if (iface)
+			nm_device_set_ip_iface (device, iface);
+
+		nm_device_activate_schedule_stage4_ip4_config_get (device);
+	}
+}
+
+static void
+modem_enabled_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+{
+	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
+
+	real_set_enabled (NM_DEVICE_INTERFACE (self), nm_modem_get_mm_enabled (priv->modem));
+
+	g_signal_emit (G_OBJECT (self), signals[ENABLE_CHANGED], 0);
+}
+
+/*****************************************************************************/
+
+NMModem *
+nm_device_modem_get_modem (NMDeviceModem *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE_MODEM (self), NULL);
+
+	return NM_DEVICE_MODEM_GET_PRIVATE (self)->modem;
+}
+
+/*****************************************************************************/
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	nm_modem_device_state_changed (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem,
+	                               new_state,
+	                               old_state,
+	                               reason);
+}
+
+static guint32
+real_get_generic_capabilities (NMDevice *device)
+{
+	return NM_DEVICE_CAP_NM_SUPPORTED;
+}
+
+static NMConnection *
+real_get_best_auto_connection (NMDevice *device,
+							   GSList *connections,
+							   char **specific_object)
+{
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
+
+	return nm_modem_get_best_auto_connection (priv->modem, connections, specific_object);
+}
+
+static void
+real_connection_secrets_updated (NMDevice *device,
+								 NMConnection *connection,
+								 GSList *updated_settings,
+								 RequestSecretsCaller caller)
+{
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
+	NMActRequest *req;
+
+	g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (device)));
+
+	req = nm_device_get_act_request (device);
+	g_assert (req);
+
+	if (!nm_modem_connection_secrets_updated (priv->modem,
+                                              req,
+                                              connection,
+                                              updated_settings,
+                                              caller)) {
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS);
+		return;
+	}
+
+	/* PPP handles stuff itself... */
+	if (caller == SECRETS_CALLER_PPP)
+		return;
+
+	/* Otherwise, on success for modem secrets we need to schedule stage1 again */
+	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
+	nm_device_activate_schedule_stage1_device_prepare (device);
+}
+
+static gboolean
+real_check_connection_compatible (NMDevice *device,
+                                  NMConnection *connection,
+                                  GError **error)
+{
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
+
+	return nm_modem_check_connection_compatible (priv->modem, connection, error);
+}
+
+static gboolean
+real_hw_is_up (NMDevice *device)
+{
+	return nm_modem_hw_is_up (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device);
+}
+
+static gboolean
+real_hw_bring_up (NMDevice *device, gboolean *no_firmware)
+{
+	return nm_modem_hw_bring_up (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device, no_firmware);
+}
+
+static void
+real_deactivate_quickly (NMDevice *device)
+{
+	nm_modem_deactivate_quickly (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device);
+}
+
+static NMActStageReturn
+real_act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+{
+	NMActRequest *req;
+
+	req = nm_device_get_act_request (device);
+	g_assert (req);
+
+	return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, req, reason);
+}
+
+static NMActStageReturn
+real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+{
+	NMActRequest *req;
+
+	req = nm_device_get_act_request (device);
+	g_assert (req);
+
+	return nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, req, reason);
+}
+
+static NMActStageReturn
+real_act_stage3_ip4_config_start (NMDevice *device, NMDeviceStateReason *reason)
+{
+	return nm_modem_stage3_ip4_config_start (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem,
+	                                         device,
+	                                         NM_DEVICE_CLASS (nm_device_modem_parent_class),
+	                                         reason);
+}
+
+static NMActStageReturn
+real_act_stage4_get_ip4_config (NMDevice *device,
+                                NMIP4Config **config,
+                                NMDeviceStateReason *reason)
+{
+	return nm_modem_stage4_get_ip4_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem,
+	                                       device,
+	                                       NM_DEVICE_CLASS (nm_device_modem_parent_class),
+	                                       config,
+	                                       reason);
+}
+
+/*****************************************************************************/
+
+static gboolean
+real_get_enabled (NMDeviceInterface *device)
+{
+	return nm_modem_get_mm_enabled (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem);
+}
+
+static void
+real_set_enabled (NMDeviceInterface *device, gboolean enabled)
+{
+	NMDeviceModem *self = NM_DEVICE_MODEM (device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
+	NMDeviceState state;
+
+	if (priv->modem) {
+		nm_modem_set_mm_enabled (priv->modem, enabled);
+
+		if (enabled == FALSE) {
+			state = nm_device_interface_get_state (device);
+			if (state == NM_DEVICE_STATE_ACTIVATED) {
+				nm_device_state_changed (NM_DEVICE (device),
+				                         NM_DEVICE_STATE_DISCONNECTED,
+				                         NM_DEVICE_STATE_REASON_NONE);
+			}
+		}
+	}
+}
+
+/*****************************************************************************/
+
+static void
+device_interface_init (NMDeviceInterface *iface_class)
+{
+    iface_class->get_enabled = real_get_enabled;
+    iface_class->set_enabled = real_set_enabled;
+}
+
+static void
+nm_device_modem_init (NMDeviceModem *self)
+{
+	g_signal_connect (self, "state-changed", G_CALLBACK (device_state_changed), self);
+}
+
+static void
+set_modem (NMDeviceModem *self, NMModem *modem)
+{
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
+
+	g_return_if_fail (modem != NULL);
+
+	priv->modem = g_object_ref (modem);
+
+	g_signal_connect (modem, NM_MODEM_PPP_STATS, G_CALLBACK (ppp_stats), self);
+	g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
+	g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
+	g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self);
+	g_signal_connect (modem, NM_MODEM_NEED_AUTH, G_CALLBACK (modem_need_auth), self);
+	g_signal_connect (modem, "notify::" NM_MODEM_ENABLED, G_CALLBACK (modem_enabled_cb), self);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	switch (prop_id) {
+	case PROP_MODEM:
+		/* construct-only */
+		set_modem (NM_DEVICE_MODEM (object), g_value_get_object (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MODEM:
+		g_value_set_object (value, priv->modem);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
+
+	g_object_unref (priv->modem);
+	priv->modem = NULL;
+
+	G_OBJECT_CLASS (nm_device_modem_parent_class)->finalize (object);
+}
+
+static void
+nm_device_modem_class_init (NMDeviceModemClass *mclass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (mclass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (mclass);
+
+	g_type_class_add_private (object_class, sizeof (NMDeviceModemPrivate));
+
+	/* Virtual methods */
+	object_class->finalize = finalize;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+
+	device_class->get_generic_capabilities = real_get_generic_capabilities;
+	device_class->get_best_auto_connection = real_get_best_auto_connection;
+	device_class->connection_secrets_updated = real_connection_secrets_updated;
+	device_class->check_connection_compatible = real_check_connection_compatible;
+	device_class->hw_is_up = real_hw_is_up;
+	device_class->hw_bring_up = real_hw_bring_up;
+	device_class->deactivate_quickly = real_deactivate_quickly;
+	device_class->act_stage1_prepare = real_act_stage1_prepare;
+	device_class->act_stage2_config = real_act_stage2_config;
+	device_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start;
+	device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_MODEM,
+		 g_param_spec_object (NM_DEVICE_MODEM_MODEM,
+		                      "Modem",
+		                      "Modem",
+		                      NM_TYPE_MODEM,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	/* Signals */
+	signals[PPP_STATS] =
+		g_signal_new ("ppp-stats",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMDeviceModemClass, ppp_stats),
+					  NULL, NULL,
+					  _nm_marshal_VOID__UINT_UINT,
+					  G_TYPE_NONE, 2,
+					  G_TYPE_UINT, G_TYPE_UINT);
+
+	signals[ENABLE_CHANGED] =
+		g_signal_new (NM_DEVICE_MODEM_ENABLE_CHANGED,
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  0,
+					  NULL, NULL,
+					  g_cclosure_marshal_VOID__VOID,
+					  G_TYPE_NONE, 0);
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (mclass),
+	                                 nm_modem_get_serial_dbus_info ());
+}
+
diff --git a/src/nm-device-modem.h b/src/nm-device-modem.h
new file mode 100644
index 00000000..c6ef4d21
--- /dev/null
+++ b/src/nm-device-modem.h
@@ -0,0 +1,56 @@
+/* -*- 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) 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_MODEM_H
+#define NM_DEVICE_MODEM_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-device.h"
+#include "nm-modem.h"
+
+#define NM_TYPE_DEVICE_MODEM            (nm_device_modem_get_type ())
+#define NM_DEVICE_MODEM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModem))
+#define NM_DEVICE_MODEM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass))
+#define NM_IS_DEVICE_MODEM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_MODEM))
+#define NM_IS_DEVICE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DEVICE_MODEM))
+#define NM_DEVICE_MODEM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass))
+
+#define NM_DEVICE_MODEM_MODEM "modem"
+
+#define NM_DEVICE_MODEM_ENABLE_CHANGED "enable-changed"
+
+typedef struct {
+	NMDevice parent;
+} NMDeviceModem;
+
+typedef struct {
+	NMDeviceClass parent;
+
+	void (*ppp_stats) (NMDeviceModem *self, guint32 in_bytes, guint32 out_bytes);
+} NMDeviceModemClass;
+
+GType nm_device_modem_get_type (void);
+
+/* Private for subclases */
+NMModem *nm_device_modem_get_modem (NMDeviceModem *self);
+
+#endif /* NM_DEVICE_MODEM_H */
diff --git a/src/nm-device-olpc-mesh.c b/src/nm-device-olpc-mesh.c
new file mode 100644
index 00000000..546ecfaf
--- /dev/null
+++ b/src/nm-device-olpc-mesh.c
@@ -0,0 +1,974 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ * Daniel Drake <dsd@laptop.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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.
+ *
+ * (C) Copyright 2005 - 2010 Red Hat, Inc.
+ * (C) Copyright 2008 Collabora Ltd.
+ * (C) Copyright 2009 One Laptop per Child
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <net/ethernet.h>
+#include <iwlib.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "nm-device.h"
+#include "nm-device-wifi.h"
+#include "nm-device-olpc-mesh.h"
+#include "nm-device-interface.h"
+#include "nm-device-private.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+#include "nm-activation-request.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-olpc-mesh.h"
+#include "nm-system.h"
+#include "nm-manager.h"
+
+/* This is a bug; but we can't really change API now... */
+#include "NetworkManagerVPN.h"
+
+
+#include "nm-device-olpc-mesh-glue.h"
+
+static void nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid);
+
+G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshPrivate))
+
+
+enum {
+	PROP_0,
+	PROP_HW_ADDRESS,
+	PROP_COMPANION,
+	PROP_ACTIVE_CHANNEL,
+
+	LAST_PROP
+};
+
+enum {
+	PROPERTIES_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef enum
+{
+	NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH = 0,
+	NM_OLPC_MESH_ERROR_CONNECTION_INVALID,
+	NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE,
+} NMOlpcMeshError;
+
+#define NM_OLPC_MESH_ERROR (nm_olpc_mesh_error_quark ())
+#define NM_TYPE_OLPC_MESH_ERROR (nm_olpc_mesh_error_get_type ())
+
+
+struct _NMDeviceOlpcMeshPrivate
+{
+	gboolean          dispose_has_run;
+
+	struct ether_addr hw_addr;
+
+	GByteArray *      ssid;
+
+	gint8			  num_freqs;
+	guint32			  freqs[IW_MAX_FREQUENCIES];
+
+	guint8			  we_version;
+	gboolean          up;
+
+	NMDevice *        companion;
+	gboolean          stage1_waiting;
+	guint             device_added_id;
+};
+
+static GQuark
+nm_olpc_mesh_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-mesh-error");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+static GType
+nm_olpc_mesh_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Connection was not a wireless connection. */
+			ENUM_ENTRY (NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH, "ConnectionNotMesh"),
+			/* Connection was not a valid wireless connection. */
+			ENUM_ENTRY (NM_OLPC_MESH_ERROR_CONNECTION_INVALID, "ConnectionInvalid"),
+			/* Connection does not apply to this device. */
+			ENUM_ENTRY (NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMOlpcMeshError", values);
+	}
+	return etype;
+}
+
+static guint32
+real_get_generic_capabilities (NMDevice *dev)
+{
+	int fd;
+	guint32 caps = NM_DEVICE_CAP_NONE;
+	struct iw_range range;
+	struct iwreq wrq;
+	const char *iface = nm_device_get_iface (dev);
+
+	/* Check for Wireless Extensions support >= 16 for wireless devices */
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_warn (LOGD_OLPC_MESH, "couldn't open control socket.");
+		goto out;
+	}
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	memset (&range, 0, sizeof (struct iw_range));
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	wrq.u.data.pointer = (caddr_t) &range;
+	wrq.u.data.length = sizeof (struct iw_range);
+
+	if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) {
+		nm_log_warn (LOGD_OLPC_MESH, "couldn't get driver range information.");
+		goto out;
+	}
+
+	if ((wrq.u.data.length < 300) || (range.we_version_compiled < 16)) {
+		nm_log_warn (LOGD_OLPC_MESH,
+		             "(%s): driver's Wireless Extensions version (%d) is too old.",
+		             iface, range.we_version_compiled);
+		goto out;
+	} else {
+		caps |= NM_DEVICE_CAP_NM_SUPPORTED;
+	}
+
+out:
+	if (fd >= 0)
+		close (fd);
+	return caps;
+}
+
+static void
+nm_device_olpc_mesh_init (NMDeviceOlpcMesh * self)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+
+	priv->dispose_has_run = FALSE;
+	priv->we_version = 0;
+	priv->companion = NULL;
+	priv->stage1_waiting = FALSE;
+
+	memset (&(priv->hw_addr), 0, sizeof (struct ether_addr));
+}
+
+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);
+}
+
+
+/* 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 GObject*
+constructor (GType type,
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	GObjectClass *klass;
+	NMDeviceOlpcMesh *self;
+	NMDeviceOlpcMeshPrivate *priv;
+	const char *iface;
+	int fd;
+	struct iw_range range;
+	struct iwreq wrq;
+	int i;
+
+	klass = G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class);
+	object = klass->constructor (type, n_construct_params, construct_params);
+	if (!object)
+		return NULL;
+
+	self = NM_DEVICE_OLPC_MESH (object);
+	priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+
+	nm_log_dbg (LOGD_HW | LOGD_OLPC_MESH, "(%s): kernel ifindex %d",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            nm_device_get_ifindex (NM_DEVICE (self)));
+
+	iface = nm_device_get_iface (NM_DEVICE (self));
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		goto error;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	memset (&range, 0, sizeof (struct iw_range));
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	wrq.u.data.pointer = (caddr_t) &range;
+	wrq.u.data.length = sizeof (struct iw_range);
+
+	if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0)
+		goto error;
+
+	priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES);
+	for (i = 0; i < priv->num_freqs; i++)
+		priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]);
+
+	priv->we_version = range.we_version_compiled;
+
+	close (fd);
+
+	/* shorter timeout for mesh connectivity */
+	nm_device_set_dhcp_timeout (NM_DEVICE (self), 20);
+	return object;
+
+error:
+	if (fd >= 0)
+		close (fd);
+	g_object_unref (object);
+	return NULL;
+}
+
+static gboolean
+real_hw_is_up (NMDevice *device)
+{
+	return nm_system_device_is_up (device);
+}
+
+static gboolean
+real_hw_bring_up (NMDevice *dev, gboolean *no_firmware)
+{
+	return nm_system_device_set_up_down (dev, TRUE, no_firmware);
+}
+
+static void
+real_hw_take_down (NMDevice *dev)
+{
+	nm_system_device_set_up_down (dev, FALSE, NULL);
+}
+
+static gboolean
+real_is_up (NMDevice *device)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+
+	return priv->up;
+}
+
+static gboolean
+real_bring_up (NMDevice *dev)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+
+	priv->up = TRUE;
+	return TRUE;
+}
+
+static void
+device_cleanup (NMDeviceOlpcMesh *self)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+
+	if (priv->ssid) {
+		g_byte_array_free (priv->ssid, TRUE);
+		priv->ssid = NULL;
+	}
+	priv->up = FALSE;
+}
+
+static void
+real_take_down (NMDevice *dev)
+{
+	device_cleanup (NM_DEVICE_OLPC_MESH (dev));
+}
+
+static gboolean
+real_check_connection_compatible (NMDevice *device,
+                                  NMConnection *connection,
+                                  GError **error)
+{
+	NMSettingConnection *s_con;
+	NMSettingOlpcMesh *s_mesh;
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_OLPC_MESH_SETTING_NAME)) {
+		g_set_error (error,
+		             NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH,
+		             "The connection was not a Mesh connection.");
+		return FALSE;
+	}
+
+	s_mesh = NM_SETTING_OLPC_MESH (nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH));
+	if (!s_mesh) {
+		g_set_error (error,
+		             NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_INVALID,
+		             "The connection was not a valid Mesh connection.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/*
+ * nm_device_olpc_mesh_get_address
+ *
+ * Get a device's hardware address
+ *
+ */
+static void
+nm_device_olpc_mesh_get_address (NMDeviceOlpcMesh *self,
+                                       struct ether_addr *addr)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (addr != NULL);
+
+	memcpy (addr, &(priv->hw_addr), sizeof (struct ether_addr));
+}
+
+static int
+create_socket_with_request (NMDevice *self, struct iwreq *req)
+{
+	int sk;
+	const char * iface;
+
+	g_return_val_if_fail (self != NULL, -1);
+
+	sk = socket (AF_INET, SOCK_DGRAM, 0);
+	if (!sk) {
+		nm_log_err (LOGD_OLPC_MESH, "Couldn't create socket: %d.", errno);
+		return -1;
+	}
+
+	memset (req, 0, sizeof (struct iwreq));
+	iface = nm_device_get_iface (NM_DEVICE (self));
+	strncpy (req->ifr_name, iface, IFNAMSIZ);
+
+	return sk;
+}
+
+static guint32
+nm_device_olpc_mesh_get_channel (NMDeviceOlpcMesh *self)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	int sk;
+	struct iwreq req;
+	int ret = 0;
+	int i;
+	guint32 freq;
+
+	sk = create_socket_with_request (NM_DEVICE (self), &req);
+	if (sk == -1)
+		return 0;
+
+	if ((ioctl (sk, SIOCGIWFREQ, &req)) != 0) {
+		nm_log_warn (LOGD_OLPC_MESH, "(%s): failed to get channel (errno: %d)",
+		             nm_device_get_iface (NM_DEVICE (self)), errno);
+		goto out;
+	}
+
+	freq = iw_freq_to_uint32 (&req.u.freq);
+
+	for (i = 0 ; i < priv->num_freqs; i++) {
+		if (freq == priv->freqs[i])
+			break;
+	}
+	if (i < priv->num_freqs)
+		ret = i + 1;
+
+out:
+	if (sk >= 0)
+		close (sk);
+	return ret;
+}
+
+static void
+nm_device_olpc_mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel)
+{
+	int sk;
+	struct iwreq req;
+
+	if (nm_device_olpc_mesh_get_channel (self) == channel)
+		return;
+
+	sk = create_socket_with_request (NM_DEVICE (self), &req);
+	if (sk < 0)
+		return;
+
+	if (channel > 0) {
+		req.u.freq.flags = IW_FREQ_FIXED;
+		req.u.freq.e = 0;
+		req.u.freq.m = channel;
+	}
+
+	if (ioctl (sk, SIOCSIWFREQ, &req) != 0) {
+		nm_log_warn (LOGD_OLPC_MESH, "(%s): failed to set to channel %d (errno: %d)",
+		             nm_device_get_iface (NM_DEVICE (self)), channel, errno);
+	} else
+		g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL);
+
+	close (sk);
+}
+
+static void
+nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	int sk;
+	struct iwreq wrq;
+	const char * iface;
+	guint32 len = 0;
+	char buf[IW_ESSID_MAX_SIZE + 1];
+
+	g_return_if_fail (self != NULL);
+
+	sk = socket (AF_INET, SOCK_DGRAM, 0);
+	if (!sk) {
+		nm_log_err (LOGD_OLPC_MESH, "Couldn't create socket: %d.", errno);
+		return;
+	}
+
+	iface = nm_device_get_iface (NM_DEVICE (self));
+
+	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;
+
+	if (priv->we_version < 21) {
+		/* For historic reasons, set SSID length to include one extra
+		 * character, C string nul termination, even though SSID is
+		 * really an octet string that should not be presented as a C
+		 * string. Some Linux drivers decrement the length by one and
+		 * can thus end up missing the last octet of the SSID if the
+		 * length is not incremented here. WE-21 changes this to
+		 * explicitly require the length _not_ to include nul
+		 * termination. */
+		if (len)
+			len++;
+	}
+	wrq.u.essid.length = len;
+	wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */
+
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+
+	if (ioctl (sk, SIOCSIWESSID, &wrq) < 0) {
+		if (errno != ENODEV) {
+			nm_log_err (LOGD_OLPC_MESH, "(%s): error setting SSID to '%s': %s",
+			            iface,
+			            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(null)",
+			            strerror (errno));
+		}
+    }
+
+	close (sk);
+}
+
+/****************************************************************************/
+
+static void
+real_update_hw_address (NMDevice *dev)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	struct ifreq req;
+	int ret, fd;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_warn (LOGD_OLPC_MESH, "could not open control socket.");
+		return;
+	}
+
+	memset (&req, 0, sizeof (struct ifreq));
+	strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+	ret = ioctl (fd, SIOCGIFHWADDR, &req);
+	if (ret) {
+		nm_log_warn (LOGD_OLPC_MESH, "(%s): error getting hardware address: %d",
+		             nm_device_get_iface (dev), errno);
+		goto out;
+	}
+
+	if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) {
+		memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr));
+		g_object_notify (G_OBJECT (dev), NM_DEVICE_OLPC_MESH_HW_ADDRESS);
+	}
+
+out:
+	close (fd);
+}
+
+
+static NMActStageReturn
+real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (dev);
+	gboolean scanning;
+
+	/* disconnect companion device, if it is connected */
+	if (nm_device_get_act_request (NM_DEVICE (priv->companion))) {
+		nm_log_info (LOGD_OLPC_MESH, "(%s): disconnecting companion device %s",
+		             nm_device_get_iface (dev),
+		             nm_device_get_iface (priv->companion));
+		/* FIXME: VPN stuff here is a bug; but we can't really change API now... */
+		nm_device_state_changed (NM_DEVICE (priv->companion),
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
+		nm_log_info (LOGD_OLPC_MESH, "(%s): companion %s disconnected",
+		             nm_device_get_iface (dev),
+		             nm_device_get_iface (priv->companion));
+	}
+
+
+	/* wait with continuing configuration untill the companion device is done scanning */
+	g_object_get (priv->companion, "scanning", &scanning, NULL);
+	if (scanning) {
+		priv->stage1_waiting = TRUE;
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+	}
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+static NMActStageReturn
+real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev);
+	NMConnection *connection;
+	NMSettingOlpcMesh *s_mesh;
+	NMActRequest *req;
+	guint32 channel;
+	const GByteArray *anycast_addr_array;
+	guint8 *anycast_addr = NULL;
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	s_mesh = NM_SETTING_OLPC_MESH (nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH));
+	g_assert (s_mesh);
+
+	channel = nm_setting_olpc_mesh_get_channel (s_mesh);
+	if (channel != 0)
+		nm_device_olpc_mesh_set_channel (self, channel);
+	nm_device_olpc_mesh_set_ssid (self, nm_setting_olpc_mesh_get_ssid (s_mesh));
+
+	anycast_addr_array = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh);
+	if (anycast_addr_array)
+		anycast_addr = anycast_addr_array->data;
+
+	nm_device_set_dhcp_anycast_address (dev, anycast_addr);
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (object);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	NMManager *manager;
+
+	if (priv->dispose_has_run) {
+		G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object);
+		return;
+	}
+	priv->dispose_has_run = TRUE;
+
+	device_cleanup (self);
+
+	manager = nm_manager_get (NULL, NULL, NULL, FALSE, FALSE, FALSE, NULL);
+	if (priv->device_added_id)
+		g_signal_handler_disconnect (manager, priv->device_added_id);
+	g_object_unref (manager);
+
+	G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH (object);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device);
+	struct ether_addr hw_addr;
+
+	switch (prop_id) {
+	case PROP_HW_ADDRESS:
+		nm_device_olpc_mesh_get_address (device, &hw_addr);
+		g_value_take_string (value, nm_ether_ntop (&hw_addr));
+		break;
+	case PROP_COMPANION:
+		if (priv->companion)
+			g_value_set_boxed (value, nm_device_get_path (priv->companion));
+		else
+			g_value_set_boxed (value, "/");
+		break;
+	case PROP_ACTIVE_CHANNEL:
+		g_value_set_uint (value, nm_device_olpc_mesh_get_channel (device));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMDeviceOlpcMeshPrivate));
+
+	object_class->constructor = constructor;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+
+	parent_class->get_type_capabilities = NULL;
+	parent_class->get_generic_capabilities = real_get_generic_capabilities;
+	parent_class->hw_is_up = real_hw_is_up;
+	parent_class->hw_bring_up = real_hw_bring_up;
+	parent_class->hw_take_down = real_hw_take_down;
+	parent_class->is_up = real_is_up;
+	parent_class->bring_up = real_bring_up;
+	parent_class->take_down = real_take_down;
+	parent_class->update_hw_address = real_update_hw_address;
+	parent_class->check_connection_compatible = real_check_connection_compatible;
+
+	parent_class->act_stage1_prepare = real_act_stage1_prepare;
+	parent_class->act_stage2_config = real_act_stage2_config;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_HW_ADDRESS,
+		 g_param_spec_string (NM_DEVICE_OLPC_MESH_HW_ADDRESS,
+		                      "MAC Address",
+		                      "Hardware MAC address",
+		                      NULL,
+		                      G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_COMPANION,
+		 g_param_spec_boxed (NM_DEVICE_OLPC_MESH_COMPANION,
+		                     "Companion device",
+		                     "Companion device object path",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE_CHANNEL,
+		 g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL,
+		                   "Active channel",
+		                   "Active channel",
+		                   0, G_MAXUINT32, 0,
+		                   G_PARAM_READABLE));
+
+	signals[PROPERTIES_CHANGED] =
+		nm_properties_changed_signal_new (object_class,
+		                                  G_STRUCT_OFFSET (NMDeviceOlpcMeshClass, properties_changed));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), &dbus_glib_nm_device_olpc_mesh_object_info);
+
+	dbus_g_error_domain_register (NM_OLPC_MESH_ERROR, NULL, 
+		NM_TYPE_OLPC_MESH_ERROR);
+}
+
+static void
+companion_notify_cb (NMDeviceWifi *companion, GParamSpec *pspec, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	gboolean scanning;
+
+	if (!priv->stage1_waiting)
+		return;
+
+	g_object_get (companion, "scanning", &scanning, NULL);
+
+	if (!scanning) {
+		priv->stage1_waiting = FALSE;
+		nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self));
+	}
+}
+
+/* disconnect from mesh if someone starts using the companion */
+static void
+companion_state_changed_cb (NMDeviceWifi *companion,
+                            NMDeviceState state,
+                            NMDeviceState old_state,
+                            NMDeviceStateReason reason,
+                            gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceState self_state = nm_device_get_state (NM_DEVICE (self));
+
+	if (   self_state < NM_DEVICE_STATE_PREPARE
+	    || self_state > NM_DEVICE_STATE_ACTIVATED
+	    || state < NM_DEVICE_STATE_PREPARE
+	    || state > NM_DEVICE_STATE_ACTIVATED)
+		return;
+
+	nm_log_dbg (LOGD_OLPC_MESH, "(%s): disconnecting mesh due to companion connectivity",
+	            nm_device_get_iface (NM_DEVICE (self)));
+	/* FIXME: VPN stuff here is a bug; but we can't really change API now... */
+	nm_device_state_changed (NM_DEVICE (self),
+	                         NM_DEVICE_STATE_DISCONNECTED,
+	                         NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
+}
+
+static gboolean
+companion_scan_allowed_cb (NMDeviceWifi *companion, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceState state;
+
+	g_object_get (G_OBJECT (self), NM_DEVICE_INTERFACE_STATE, &state, NULL);
+
+	/* Don't allow the companion to scan while configure the mesh interface */
+	return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_IP_CONFIG);
+}
+
+static gboolean
+companion_autoconnect_allowed_cb (NMDeviceWifi *companion, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceState state;
+
+	g_object_get (G_OBJECT (self), NM_DEVICE_INTERFACE_STATE, &state, NULL);
+
+	/* Don't allow the companion to autoconnect while a mesh connection is
+	 * active */
+	return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_ACTIVATED);
+}
+
+static gboolean
+is_companion (NMDeviceOlpcMesh *self, NMDevice *other)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	struct ether_addr their_addr;
+	NMManager *manager;
+
+	if (!NM_IS_DEVICE_WIFI (other))
+		return FALSE;
+
+	nm_device_wifi_get_address (NM_DEVICE_WIFI (other), &their_addr);
+
+	if (memcmp (priv->hw_addr.ether_addr_octet,
+		their_addr.ether_addr_octet, ETH_ALEN) != 0) {
+		return FALSE;
+	}
+
+	/* FIXME detect when our companion leaves */
+	priv->companion = other;
+
+	/* When we've found the companion, stop listening for other devices */
+	manager = nm_manager_get (NULL, NULL, NULL, FALSE, FALSE, FALSE, NULL);
+	if (priv->device_added_id) {
+		g_signal_handler_disconnect (manager, priv->device_added_id);
+		priv->device_added_id = 0;
+	}
+	g_object_unref (manager);
+
+	nm_device_state_changed (NM_DEVICE (self),
+	                         NM_DEVICE_STATE_DISCONNECTED,
+	                         NM_DEVICE_STATE_REASON_NONE);
+
+	nm_log_info (LOGD_OLPC_MESH, "(%s): found companion WiFi device %s",
+	             nm_device_get_iface (NM_DEVICE (self)),
+	             nm_device_get_iface (other));
+
+	g_signal_connect (G_OBJECT (other), "state-changed",
+	                  G_CALLBACK (companion_state_changed_cb), self);
+	g_signal_connect (G_OBJECT (other), "notify::scanning",
+	                  G_CALLBACK (companion_notify_cb), self);
+	g_signal_connect (G_OBJECT (other), "scanning-allowed",
+	                  G_CALLBACK (companion_scan_allowed_cb), self);
+	g_signal_connect (G_OBJECT (other), "autoconnect-allowed",
+	                  G_CALLBACK (companion_autoconnect_allowed_cb), self);
+
+	g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_COMPANION);
+
+	return TRUE;
+}
+
+static void
+device_added_cb (NMManager *manager, NMDevice *other, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+
+	is_companion (self, other);
+}
+
+static gboolean
+check_companion_cb (gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	NMManager *manager;
+	GSList *list;
+
+	if (priv->companion != NULL) {
+		nm_device_state_changed (NM_DEVICE (user_data),
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_REASON_NONE);
+		return FALSE;
+	}
+
+	if (priv->device_added_id != 0)
+		return FALSE;
+
+	manager = nm_manager_get (NULL, NULL, NULL, FALSE, FALSE, FALSE, NULL);
+
+	priv->device_added_id = g_signal_connect (manager, "device-added",
+	                                          G_CALLBACK (device_added_cb), self);
+
+	/* Try to find the companion if it's already known to the NMManager */
+	for (list = nm_manager_get_devices (manager); list ; list = g_slist_next (list)) {
+		if (is_companion (self, NM_DEVICE (list->data)))
+			break;
+	}
+
+	g_object_unref (manager);
+
+	return FALSE;
+}
+
+static void
+state_changed_cb (NMDevice *device, NMDeviceState state, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
+
+	switch (state) {
+	case NM_DEVICE_STATE_UNMANAGED:
+		break;
+	case NM_DEVICE_STATE_UNAVAILABLE:
+		/* If transitioning to UNAVAILBLE and the companion device is known then
+		 * transition to DISCONNECTED otherwise wait for our companion.
+		 */
+		g_idle_add (check_companion_cb, self);
+		break;
+	case NM_DEVICE_STATE_ACTIVATED:
+		break;
+	case NM_DEVICE_STATE_FAILED:
+		break;
+	case NM_DEVICE_STATE_DISCONNECTED:
+		break;
+	default:
+		break;
+	}
+}
+
+
+NMDevice *
+nm_device_olpc_mesh_new (const char *udi,
+                         const char *iface,
+                         const char *driver)
+{
+	GObject *obj;
+
+	g_return_val_if_fail (udi != NULL, NULL);
+	g_return_val_if_fail (iface != NULL, NULL);
+	g_return_val_if_fail (driver != NULL, NULL);
+
+	obj = g_object_new (NM_TYPE_DEVICE_OLPC_MESH,
+	                    NM_DEVICE_INTERFACE_UDI, udi,
+	                    NM_DEVICE_INTERFACE_IFACE, iface,
+	                    NM_DEVICE_INTERFACE_DRIVER, driver,
+	                    NM_DEVICE_INTERFACE_TYPE_DESC, "802.11 OLPC Mesh",
+	                    NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_OLPC_MESH,
+	                    NULL);
+	if (obj == NULL)
+		return NULL;
+
+	g_signal_connect (obj, "state-changed", G_CALLBACK (state_changed_cb), NULL);
+
+	return NM_DEVICE (obj);
+}
diff --git a/src/nm-device-olpc-mesh.h b/src/nm-device-olpc-mesh.h
new file mode 100644
index 00000000..bcc03e57
--- /dev/null
+++ b/src/nm-device-olpc-mesh.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/* NetworkManager -- Network link manager
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ * Daniel Drake <dsd@laptop.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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.
+ *
+ * (C) Copyright 2005 Red Hat, Inc.
+ * (C) Copyright 2008 Collabora Ltd.
+ * (C) Copyright 2009 One Laptop per Child
+ */
+
+#ifndef NM_DEVICE_OLPC_MESH_H
+#define NM_DEVICE_OLPC_MESH_H
+
+#include <glib-object.h>
+#include <dbus/dbus.h>
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_OLPC_MESH            (nm_device_olpc_mesh_get_type ())
+#define NM_DEVICE_OLPC_MESH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh))
+#define NM_DEVICE_OLPC_MESH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass))
+#define NM_IS_DEVICE_OLPC_MESH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OLPC_MESH))
+#define NM_IS_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_OLPC_MESH))
+#define NM_DEVICE_OLPC_MESH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass))
+
+#define NM_DEVICE_OLPC_MESH_HW_ADDRESS     "hw-address"
+#define NM_DEVICE_OLPC_MESH_COMPANION      "companion"
+#define NM_DEVICE_OLPC_MESH_BITRATE        "bitrate"
+#define NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL "active-channel"
+
+#ifndef NM_DEVICE_OLPC_MESH_DEFINED
+#define NM_DEVICE_OLPC_MESH_DEFINED
+typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh;
+#endif
+
+typedef struct _NMDeviceOlpcMeshClass NMDeviceOlpcMeshClass;
+typedef struct _NMDeviceOlpcMeshPrivate NMDeviceOlpcMeshPrivate;
+
+struct _NMDeviceOlpcMesh
+{
+	NMDevice parent;
+};
+
+struct _NMDeviceOlpcMeshClass
+{
+	NMDeviceClass parent;
+
+	/* Signals */
+	void (*properties_changed) (NMDeviceOlpcMesh *device,
+	                            GHashTable *properties);
+};
+
+
+GType nm_device_olpc_mesh_get_type (void);
+
+NMDevice *nm_device_olpc_mesh_new (const char *udi,
+                                   const char *iface,
+                                   const char *driver);
+
+G_END_DECLS
+
+#endif  /* NM_DEVICE_OLPC_MESH_H */
diff --git a/src/nm-device-private.h b/src/nm-device-private.h
new file mode 100644
index 00000000..371f17f1
--- /dev/null
+++ b/src/nm-device-private.h
@@ -0,0 +1,45 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_PRIVATE_H
+#define NM_DEVICE_PRIVATE_H
+
+#include "nm-device.h"
+
+void nm_device_set_ip_iface (NMDevice *self, const char *iface);
+
+void nm_device_activate_schedule_stage3_ip_config_start (NMDevice *device);
+
+void nm_device_state_changed (NMDevice *device,
+                              NMDeviceState state,
+                              NMDeviceStateReason reason);
+
+gboolean nm_device_hw_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmware);
+
+void nm_device_hw_take_down (NMDevice *self, gboolean block);
+
+void nm_device_handle_autoip4_event (NMDevice *self,
+                                     const char *event,
+                                     const char *address);
+
+gboolean nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6);
+
+#endif	/* NM_DEVICE_PRIVATE_H */
diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c
new file mode 100644
index 00000000..36be9e3e
--- /dev/null
+++ b/src/nm-device-wifi.c
@@ -0,0 +1,3936 @@
+/* -*- 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 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <net/ethernet.h>
+#include <iwlib.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "nm-glib-compat.h"
+#include "nm-device.h"
+#include "nm-device-wifi.h"
+#include "nm-device-interface.h"
+#include "nm-device-private.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "nm-marshal.h"
+#include "NetworkManagerUtils.h"
+#include "nm-activation-request.h"
+#include "nm-supplicant-manager.h"
+#include "nm-supplicant-interface.h"
+#include "nm-supplicant-config.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-system.h"
+
+static gboolean impl_device_get_access_points (NMDeviceWifi *device,
+                                               GPtrArray **aps,
+                                               GError **err);
+
+#include "nm-device-wifi-glue.h"
+
+
+/* All of these are in seconds */
+#define SCAN_INTERVAL_MIN 0
+#define SCAN_INTERVAL_STEP 20
+#define SCAN_INTERVAL_MAX 120
+
+#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries"
+
+static void device_interface_init (NMDeviceInterface *iface_class);
+
+G_DEFINE_TYPE_EXTENDED (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE, device_interface_init))
+
+#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate))
+
+
+enum {
+	PROP_0,
+	PROP_HW_ADDRESS,
+	PROP_MODE,
+	PROP_BITRATE,
+	PROP_ACTIVE_ACCESS_POINT,
+	PROP_CAPABILITIES,
+	PROP_SCANNING,
+	PROP_IPW_RFKILL_STATE,
+
+	LAST_PROP
+};
+
+enum {
+	ACCESS_POINT_ADDED,
+	ACCESS_POINT_REMOVED,
+	HIDDEN_AP_FOUND,
+	PROPERTIES_CHANGED,
+	SCANNING_ALLOWED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef enum {
+	NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0,
+	NM_WIFI_ERROR_CONNECTION_INVALID,
+	NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE,
+} NMWifiError;
+
+#define NM_WIFI_ERROR (nm_wifi_error_quark ())
+#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ()) 
+
+typedef struct SupplicantStateTask {
+	NMDeviceWifi *self;
+	guint32 new_state;
+	guint32 old_state;
+	gboolean mgr_task;
+	guint source_id;
+} SupplicantStateTask;
+
+typedef struct Supplicant {
+	NMSupplicantManager *mgr;
+	NMSupplicantInterface *iface;
+
+	/* signal handler ids */
+	guint mgr_state_id;
+	guint iface_error_id;
+	guint iface_state_id;
+	guint iface_scanned_ap_id;
+	guint iface_scan_request_result_id;
+	guint iface_scan_results_id;
+	guint iface_con_state_id;
+	guint iface_notify_scanning_id;
+
+	/* Timeouts and idles */
+	guint iface_con_error_cb_id;
+	guint con_timeout_id;
+
+	GSList *mgr_tasks;
+	GSList *iface_tasks;
+} Supplicant;
+
+struct _NMDeviceWifiPrivate {
+	gboolean          disposed;
+
+	struct ether_addr hw_addr;
+
+	/* Legacy rfkill for ipw2x00; will be fixed with 2.6.33 kernel */
+	char *            ipw_rfkill_path;
+	guint             ipw_rfkill_id;
+	RfKillState       ipw_rfkill_state;
+
+	GByteArray *      ssid;
+	gint8             invalid_strength_counter;
+	iwqual            max_qual;
+
+	gint8             num_freqs;
+	guint32           freqs[IW_MAX_FREQUENCIES];
+
+	GSList *          ap_list;
+	NMAccessPoint *   current_ap;
+	guint32           rate;
+	gboolean          enabled; /* rfkilled or not */
+	
+	glong             scheduled_scan_time;
+	guint8            scan_interval; /* seconds */
+	guint             pending_scan_id;
+
+	Supplicant        supplicant;
+
+	guint32           failed_link_count;
+	guint             periodic_source_id;
+	guint             link_timeout_id;
+
+	/* Static options from driver */
+	guint8            we_version;
+	guint32           capabilities;
+	gboolean          has_scan_capa_ssid;
+};
+
+static guint32 nm_device_wifi_get_frequency (NMDeviceWifi *self);
+
+static gboolean request_wireless_scan (gpointer user_data);
+
+static void schedule_scan (NMDeviceWifi *self, gboolean backoff);
+
+static void cancel_pending_scan (NMDeviceWifi *self);
+
+static int wireless_qual_to_percent (const struct iw_quality *qual,
+                                     const struct iw_quality *max_qual);
+
+static void cleanup_association_attempt (NMDeviceWifi * self,
+                                         gboolean disconnect);
+
+static void remove_supplicant_timeouts (NMDeviceWifi *self);
+
+static void supplicant_iface_state_cb (NMSupplicantInterface * iface,
+                                       guint32 new_state,
+                                       guint32 old_state,
+                                       NMDeviceWifi *self);
+
+static void supplicant_iface_connection_state_cb (NMSupplicantInterface * iface,
+                                                  guint32 new_state,
+                                                  guint32 old_state,
+                                                  NMDeviceWifi *self);
+
+static void supplicant_iface_scanned_ap_cb (NMSupplicantInterface * iface,
+                                            GHashTable *properties,
+                                            NMDeviceWifi * self);
+
+static void supplicant_iface_scan_request_result_cb (NMSupplicantInterface * iface,
+                                                     gboolean success,
+                                                     NMDeviceWifi * self);
+
+static void supplicant_iface_scan_results_cb (NMSupplicantInterface * iface,
+                                              guint32 num_bssids,
+                                              NMDeviceWifi * self);
+
+static void supplicant_mgr_state_cb (NMSupplicantInterface * iface,
+                                     guint32 new_state,
+                                     guint32 old_state,
+                                     NMDeviceWifi *self);
+
+static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface,
+                                                 GParamSpec * pspec,
+                                                 NMDeviceWifi * self);
+
+static guint32 nm_device_wifi_get_bitrate (NMDeviceWifi *self);
+
+static void cull_scan_list (NMDeviceWifi *self);
+
+static GQuark
+nm_wifi_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-wifi-error");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+static GType
+nm_wifi_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Connection was not a wireless connection. */
+			ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS, "ConnectionNotWireless"),
+			/* Connection was not a valid wireless connection. */
+			ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INVALID, "ConnectionInvalid"),
+			/* Connection does not apply to this device. */
+			ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMWifiError", values);
+	}
+	return etype;
+}
+
+/* IPW rfkill handling (until 2.6.33) */
+RfKillState
+nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	char *contents = NULL;
+	RfKillState state = RFKILL_UNBLOCKED;
+	const char *str_state = NULL;
+
+	if (   priv->ipw_rfkill_path
+	    && g_file_get_contents (priv->ipw_rfkill_path, &contents, NULL, NULL)) {
+		contents = g_strstrip (contents);
+
+		/* 0 - RF kill not enabled
+		 * 1 - SW based RF kill active (sysfs)
+		 * 2 - HW based RF kill active
+		 * 3 - Both HW and SW baed RF kill active
+		 */
+		switch (contents[0]) {
+		case '1':
+			state = RFKILL_SOFT_BLOCKED;
+			str_state = "soft-blocked";
+			break;
+		case '2':
+		case '3':
+			state = RFKILL_HARD_BLOCKED;
+			str_state = "hard-blocked";
+			break;
+		case '0':
+			str_state = "unblocked";
+		default:
+			break;
+		}
+		g_free (contents);
+
+		nm_log_dbg (LOGD_RFKILL, "(%s): ipw rfkill state '%s'",
+		            nm_device_get_iface (NM_DEVICE (self)),
+		            str_state ? str_state : "(unknown)");
+	}
+
+	return state;
+}
+
+static gboolean
+ipw_rfkill_state_work (gpointer user_data)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	RfKillState old_state;
+
+	old_state = priv->ipw_rfkill_state;
+	priv->ipw_rfkill_state = nm_device_wifi_get_ipw_rfkill_state (self);
+	if (priv->ipw_rfkill_state != old_state)
+		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_IPW_RFKILL_STATE);
+
+	return TRUE;
+}
+
+/*
+ * nm_device_wifi_update_signal_strength
+ *
+ * Update the device's idea of the strength of its connection to the
+ * current access point.
+ *
+ */
+static void
+nm_device_wifi_update_signal_strength (NMDeviceWifi *self,
+                                       NMAccessPoint *ap)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	const char *iface = nm_device_get_iface (NM_DEVICE (self));
+	int fd, percent = -1;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd >= 0) {
+		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, iface, IFNAMSIZ);
+
+		if (ioctl (fd, SIOCGIWSTATS, &wrq) == 0)
+			percent = wireless_qual_to_percent (&stats.qual, &priv->max_qual);
+		close (fd);
+	}
+
+	/* Try to smooth out the strength.  Atmel cards, for example, will give no strength
+	 * one second and normal strength the next.
+	 */
+	if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
+		nm_ap_set_strength (ap, (gint8) percent);
+		priv->invalid_strength_counter = 0;
+	}
+}
+
+
+static gboolean
+wireless_get_range (NMDeviceWifi *self,
+                    struct iw_range *range,
+                    guint32 *response_len)
+{
+	int fd, i = 26;
+	gboolean success = FALSE;
+	const char *iface;
+	struct iwreq wrq;
+
+	g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), FALSE);
+	g_return_val_if_fail (range != NULL, FALSE);
+
+	iface = nm_device_get_iface (NM_DEVICE (self));
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface);
+		return FALSE;
+	}
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	strncpy (wrq.ifr_name, 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 (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).",
+			            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.",
+		             iface);
+	}
+
+	close (fd);
+	return success;
+}
+
+static guint32
+real_get_generic_capabilities (NMDevice *dev)
+{
+	int fd, err;
+	guint32 caps = NM_DEVICE_CAP_NONE, response_len = 0;
+	struct iwreq wrq;
+	struct iw_range range;
+	const char *iface = nm_device_get_iface (dev);
+	gboolean success;
+
+	memset (&range, 0, sizeof (struct iw_range));
+	success = wireless_get_range (NM_DEVICE_WIFI (dev), &range, &response_len);
+	if (!success)
+		return NM_DEVICE_CAP_NONE;
+
+	/* Check for Wireless Extensions support >= 16 for wireless devices */
+	if ((response_len < 300) || (range.we_version_compiled < 16)) {
+		nm_log_err (LOGD_HW | LOGD_WIFI,
+		            "(%s): driver's Wireless Extensions version (%d) is too old.",
+					iface, range.we_version_compiled);
+		return NM_DEVICE_CAP_NONE;
+	}
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface);
+		goto out;
+	}
+
+	/* Cards that don't scan aren't supported */
+	memset (&wrq, 0, sizeof (struct iwreq));
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+	err = ioctl (fd, SIOCSIWSCAN, &wrq);
+	close (fd);
+	if ((err < 0) && (errno == EOPNOTSUPP))
+		caps = NM_DEVICE_CAP_NONE;
+	else
+		caps |= NM_DEVICE_CAP_NM_SUPPORTED;
+
+out:
+	return caps;
+}
+
+#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
+get_wireless_capabilities (NMDeviceWifi *self,
+                           iwrange * range,
+                           guint32 data_len)
+{
+	guint32 minlen;
+	guint32 caps = NM_WIFI_DEVICE_CAP_NONE;
+	const char * iface;
+
+	g_return_val_if_fail (self != NULL, NM_WIFI_DEVICE_CAP_NONE);
+	g_return_val_if_fail (range != NULL, NM_WIFI_DEVICE_CAP_NONE);
+
+	iface = nm_device_get_iface (NM_DEVICE (self));
+
+	minlen = ((char *) &range->enc_capa) - (char *) range + sizeof (range->enc_capa);
+
+	/* All drivers should support WEP by default */
+	caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104;
+
+	if ((data_len >= minlen) && range->we_version_compiled >= 18) {
+		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.", 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.", iface);
+			caps &= ~WPA_CAPS;
+		}
+	}
+
+	return caps;
+}
+
+
+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);
+}
+
+
+/* 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 GObject*
+constructor (GType type,
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	GObjectClass *klass;
+	NMDeviceWifi *self;
+	NMDeviceWifiPrivate *priv;
+	struct iw_range range;
+	struct iw_range_with_scan_capa *scan_capa_range;
+	guint32 response_len = 0;
+	gboolean success;
+	int i;
+
+	klass = G_OBJECT_CLASS (nm_device_wifi_parent_class);
+	object = klass->constructor (type, n_construct_params, construct_params);
+	if (!object)
+		return NULL;
+
+	self = NM_DEVICE_WIFI (object);
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	nm_log_dbg (LOGD_HW | LOGD_WIFI, "(%s): kernel ifindex %d",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            nm_device_get_ifindex (NM_DEVICE (self)));
+
+	memset (&range, 0, sizeof (struct iw_range));
+	success = wireless_get_range (NM_DEVICE_WIFI (object), &range, &response_len);
+	if (!success)
+		goto error;
+
+	priv->max_qual.qual = range.max_qual.qual;
+	priv->max_qual.level = range.max_qual.level;
+	priv->max_qual.noise = range.max_qual.noise;
+	priv->max_qual.updated = range.max_qual.updated;
+
+	priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES);
+	for (i = 0; i < priv->num_freqs; i++)
+		priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]);
+
+	priv->we_version = range.we_version_compiled;
+
+	/* 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) {
+		priv->has_scan_capa_ssid = TRUE;
+		nm_log_info (LOGD_HW | LOGD_WIFI,
+		             "(%s): driver supports SSID scans (scan_capa 0x%02X).",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             scan_capa_range->scan_capa);
+	} else {
+		nm_log_info (LOGD_HW | LOGD_WIFI,
+		             "(%s): driver does not support SSID scans (scan_capa 0x%02X).",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             scan_capa_range->scan_capa);
+	}
+
+	/* 802.11 wireless-specific capabilities */
+	priv->capabilities = get_wireless_capabilities (self, &range, response_len);
+
+	/* Connect to the supplicant manager */
+	priv->supplicant.mgr = nm_supplicant_manager_get ();
+	priv->supplicant.mgr_state_id = g_signal_connect (priv->supplicant.mgr,
+	                                                  "state",
+	                                                  G_CALLBACK (supplicant_mgr_state_cb),
+	                                                  self);
+
+	/* The ipw2x00 drivers don't integrate with the kernel rfkill subsystem until
+	 * 2.6.33.  Thus all our nice libgudev magic is useless.  So we get to poll.
+	 *
+	 * FIXME: when 2.6.33 comes lands, we can do some sysfs parkour to figure out
+	 * if we need to poll or not by matching /sys/class/net/ethX/device to one
+	 * of the /sys/class/rfkill/rfkillX/device links.  If there's a match, we
+	 * don't have to poll.
+	 */
+	priv->ipw_rfkill_path = g_strdup_printf ("/sys/class/net/%s/device/rf_kill",
+	                                         nm_device_get_iface (NM_DEVICE (self)));
+	if (!g_file_test (priv->ipw_rfkill_path, G_FILE_TEST_IS_REGULAR)) {
+		g_free (priv->ipw_rfkill_path);
+		priv->ipw_rfkill_path = NULL;
+	}
+	priv->ipw_rfkill_state = nm_device_wifi_get_ipw_rfkill_state (self);
+
+	return object;
+
+error:
+	g_object_unref (object);
+	return NULL;
+}
+
+static gboolean
+supplicant_interface_acquire (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	guint id, mgr_state;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (priv->supplicant.mgr != NULL, FALSE);
+	/* interface already acquired? */
+	g_return_val_if_fail (priv->supplicant.iface == NULL, TRUE);
+
+	mgr_state = nm_supplicant_manager_get_state (priv->supplicant.mgr);
+	g_return_val_if_fail (mgr_state == NM_SUPPLICANT_MANAGER_STATE_IDLE, FALSE);
+
+	priv->supplicant.iface = nm_supplicant_manager_get_iface (priv->supplicant.mgr,
+	                                                          nm_device_get_iface (NM_DEVICE (self)),
+	                                                          TRUE);
+	if (priv->supplicant.iface == NULL) {
+		nm_log_err (LOGD_WIFI, "Couldn't initialize supplicant interface for %s.",
+		            nm_device_get_iface (NM_DEVICE (self)));
+		return FALSE;
+	}
+
+	id = g_signal_connect (priv->supplicant.iface,
+	                       "state",
+	                       G_CALLBACK (supplicant_iface_state_cb),
+	                       self);
+	priv->supplicant.iface_state_id = id;
+
+	id = g_signal_connect (priv->supplicant.iface,
+	                       "scanned-ap",
+	                       G_CALLBACK (supplicant_iface_scanned_ap_cb),
+	                       self);
+	priv->supplicant.iface_scanned_ap_id = id;
+
+	id = g_signal_connect (priv->supplicant.iface,
+	                       "scan-req-result",
+	                       G_CALLBACK (supplicant_iface_scan_request_result_cb),
+	                       self);
+	priv->supplicant.iface_scan_request_result_id = id;
+
+	id = g_signal_connect (priv->supplicant.iface,
+	                       "scan-results",
+	                       G_CALLBACK (supplicant_iface_scan_results_cb),
+	                       self);
+	priv->supplicant.iface_scan_results_id = id;
+
+	id = g_signal_connect (priv->supplicant.iface,
+	                       "connection-state",
+	                       G_CALLBACK (supplicant_iface_connection_state_cb),
+	                       self);
+	priv->supplicant.iface_con_state_id = id;
+
+	id = g_signal_connect (priv->supplicant.iface,
+	                       "notify::scanning",
+	                       G_CALLBACK (supplicant_iface_notify_scanning_cb),
+	                       self);
+	priv->supplicant.iface_notify_scanning_id = id;
+
+	return TRUE;
+}
+
+static void
+finish_supplicant_task (SupplicantStateTask *task, gboolean remove_source)
+{
+	NMDeviceWifi *self = task->self;
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	/* idle/timeout handlers should pass FALSE for remove_source, since they
+	 * will tell glib to remove their source from the mainloop by returning
+	 * FALSE when they exit.  When called from this NMDevice's dispose handler,
+	 * remove_source should be TRUE to cancel all outstanding idle/timeout
+	 * handlers asynchronously.
+	 */
+	if (task->source_id && remove_source)
+		g_source_remove (task->source_id);
+
+	if (task->mgr_task)
+		priv->supplicant.mgr_tasks = g_slist_remove (priv->supplicant.mgr_tasks, task);
+	else
+		priv->supplicant.iface_tasks = g_slist_remove (priv->supplicant.iface_tasks, task);
+
+	memset (task, 0, sizeof (SupplicantStateTask));
+	g_slice_free (SupplicantStateTask, task);
+}
+
+static void
+remove_supplicant_interface_error_handler (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (!priv->supplicant.iface)
+		return;
+
+	if (priv->supplicant.iface_error_id > 0) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_error_id);
+		priv->supplicant.iface_error_id = 0;
+	}
+
+	if (priv->supplicant.iface_con_error_cb_id > 0) {
+		g_source_remove (priv->supplicant.iface_con_error_cb_id);
+		priv->supplicant.iface_con_error_cb_id = 0;
+	}
+}
+
+static void
+supplicant_interface_release (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv;
+
+	g_return_if_fail (self != NULL);
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	cancel_pending_scan (self);
+
+	/* Reset the scan interval to be pretty frequent when disconnected */
+	priv->scan_interval = SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP;
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): reset scanning interval to %d seconds",
+	            nm_device_get_iface (NM_DEVICE (self)));
+
+	remove_supplicant_interface_error_handler (self);
+
+	/* Clean up all pending supplicant interface state idle tasks */
+	while (priv->supplicant.iface_tasks)
+		finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE);
+
+	if (priv->supplicant.iface_state_id > 0) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_state_id);
+		priv->supplicant.iface_state_id = 0;
+	}
+
+	if (priv->supplicant.iface_scanned_ap_id > 0) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scanned_ap_id);
+		priv->supplicant.iface_scanned_ap_id = 0;
+	}
+
+	if (priv->supplicant.iface_scan_request_result_id > 0) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scan_request_result_id);
+		priv->supplicant.iface_scan_request_result_id = 0;
+	}
+
+	if (priv->supplicant.iface_scan_results_id > 0) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scan_results_id);
+		priv->supplicant.iface_scan_results_id = 0;
+	}
+
+	if (priv->supplicant.iface_con_state_id > 0) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_con_state_id);
+		priv->supplicant.iface_con_state_id = 0;
+	}
+
+	if (priv->supplicant.iface_notify_scanning_id > 0) {
+		g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_notify_scanning_id);
+		priv->supplicant.iface_notify_scanning_id = 0;
+	}
+
+	if (priv->supplicant.iface) {
+		/* Tell the supplicant to disconnect from the current AP */
+		nm_supplicant_interface_disconnect (priv->supplicant.iface);
+
+		nm_supplicant_manager_release_iface (priv->supplicant.mgr, priv->supplicant.iface);
+		priv->supplicant.iface = NULL;
+	}
+}
+
+static NMAccessPoint *
+get_active_ap (NMDeviceWifi *self,
+               NMAccessPoint *ignore_ap,
+               gboolean match_hidden)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	const char *iface = nm_device_get_iface (NM_DEVICE (self));
+	struct ether_addr bssid;
+	const GByteArray *ssid;
+	GSList *iter;
+	int i = 0;
+	NMAccessPoint *match_nofreq = NULL;
+	gboolean found_a_band = FALSE;
+	gboolean found_bg_band = FALSE;
+	NM80211Mode devmode;
+	guint32 devfreq;
+
+	nm_device_wifi_get_bssid (self, &bssid);
+	nm_log_dbg (LOGD_WIFI, "(%s): active BSSID: %02x:%02x:%02x:%02x:%02x:%02x",
+	            iface,
+	            bssid.ether_addr_octet[0], bssid.ether_addr_octet[1],
+	            bssid.ether_addr_octet[2], bssid.ether_addr_octet[3],
+	            bssid.ether_addr_octet[4], bssid.ether_addr_octet[5]);
+
+	if (!nm_ethernet_address_is_valid (&bssid))
+		return NULL;
+
+	ssid = nm_device_wifi_get_ssid (self);
+	nm_log_dbg (LOGD_WIFI, "(%s): active SSID: %s%s%s",
+	            iface,
+	            ssid ? "'" : "",
+	            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
+	            ssid ? "'" : "");
+
+	devmode = nm_device_wifi_get_mode (self);
+	devfreq = nm_device_wifi_get_frequency (self);
+
+	/* When matching hidden APs, do a second pass that ignores the SSID check,
+	 * because NM might not yet know the SSID of the hidden AP in the scan list
+	 * and therefore it won't get matched the first time around.
+	 */
+	while (i++ < (match_hidden ? 2 : 1)) {
+		nm_log_dbg (LOGD_WIFI, "  Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : "");
+
+		/* Find this SSID + BSSID in the device's AP list */
+		for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
+			NMAccessPoint *ap = NM_AP (iter->data);
+			const struct ether_addr	*ap_bssid = nm_ap_get_address (ap);
+			const GByteArray *ap_ssid = nm_ap_get_ssid (ap);
+			NM80211Mode apmode;
+			guint32 apfreq;
+
+			nm_log_dbg (LOGD_WIFI, "    AP: %s%s%s  %02x:%02x:%02x:%02x:%02x:%02x",
+			            ap_ssid ? "'" : "",
+			            ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
+			            ap_ssid ? "'" : "",
+			            ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1],
+			            ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3],
+			            ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]);
+
+			if (ignore_ap && (ap == ignore_ap)) {
+				nm_log_dbg (LOGD_WIFI, "      ignored");
+				continue;
+			}
+
+			if (memcmp (bssid.ether_addr_octet, ap_bssid->ether_addr_octet, ETH_ALEN)) {
+				nm_log_dbg (LOGD_WIFI, "      BSSID mismatch");
+				continue;
+			}
+
+			if ((i == 0) && !nm_utils_same_ssid (ssid, ap_ssid, TRUE)) {
+				nm_log_dbg (LOGD_WIFI, "      SSID mismatch");
+				continue;
+			}
+
+			apmode = nm_ap_get_mode (ap);
+			if (devmode != apmode) {
+				nm_log_dbg (LOGD_WIFI, "      mode mismatch (device %d, ap %d)",
+				            devmode, apmode);
+				continue;
+			}
+
+			apfreq = nm_ap_get_freq (ap);
+			if (devfreq != apfreq) {
+				nm_log_dbg (LOGD_WIFI, "      frequency mismatch (device %u, ap %u)",
+				            devfreq, apfreq);
+
+				if (match_nofreq == NULL)
+					match_nofreq = ap;
+
+				if (apfreq > 4000)
+					found_a_band = TRUE;
+				else if (apfreq > 2000)
+					found_bg_band = TRUE;
+				continue;
+			}
+
+			// FIXME: handle security settings here too
+			nm_log_dbg (LOGD_WIFI, "      matched");
+			return ap;
+		}
+	}
+
+	/* Some proprietary drivers (wl.o) report tuned frequency (like when
+	 * scanning) instead of the associated AP's frequency.  This is a great
+	 * example of how WEXT is underspecified.  We use frequency to find the
+	 * active AP in the scan list because some configurations use the same
+	 * SSID/BSSID on the 2GHz and 5GHz bands simultaneously, and we need to
+	 * make sure we get the right AP in the right band.  This configuration
+	 * is uncommon though, and the frequency check penalizes closed drivers we
+	 * can't fix.  Because we're not total dicks, ignore the frequency condition
+	 * if the associated BSSID/SSID exists only in one band since that's most
+	 * likely the AP we want.
+	 */
+	if (match_nofreq && (found_a_band != found_bg_band)) {
+		const struct ether_addr	*ap_bssid = nm_ap_get_address (match_nofreq);
+		const GByteArray *ap_ssid = nm_ap_get_ssid (match_nofreq);
+
+		nm_log_dbg (LOGD_WIFI, "    matched %s%s%s  %02x:%02x:%02x:%02x:%02x:%02x",
+		            ap_ssid ? "'" : "",
+		            ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
+		            ap_ssid ? "'" : "",
+		            ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1],
+		            ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3],
+		            ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]);
+
+		return match_nofreq;
+	}
+
+	nm_log_dbg (LOGD_WIFI, "  No matching AP found.");
+	return NULL;
+}
+
+static void
+set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap)
+{
+	NMDeviceWifiPrivate *priv;
+	char *old_path = NULL;
+	NMAccessPoint *old_ap;
+
+	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	old_ap = priv->current_ap;
+
+	if (old_ap) {
+		old_path = g_strdup (nm_ap_get_dbus_path (old_ap));
+		priv->current_ap = NULL;
+	}
+
+	if (new_ap) {
+		priv->current_ap = g_object_ref (new_ap);
+
+		/* Move the current AP to the front of the scan list.  Since we
+		 * do a lot of searches looking for the current AP, it saves
+		 * time to have it in front.
+		 */
+		priv->ap_list = g_slist_remove (priv->ap_list, new_ap);
+		priv->ap_list = g_slist_prepend (priv->ap_list, new_ap);
+	}
+
+	/* Unref old AP here to ensure object lives if new_ap == old_ap */
+	if (old_ap)
+		g_object_unref (old_ap);
+
+	/* Only notify if it's really changed */
+	if (   (!old_path && new_ap)
+	    || (old_path && !new_ap)
+	    || (old_path && new_ap && strcmp (old_path, nm_ap_get_dbus_path (new_ap))))
+		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT);
+
+	g_free (old_path);
+}
+
+static void
+periodic_update (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMAccessPoint *new_ap;
+	guint32 new_rate;
+
+	/* In IBSS mode, most newer firmware/drivers do "BSS coalescing" where
+	 * multiple IBSS stations using the same SSID will eventually switch to
+	 * using the same BSSID to avoid network segmentation.  When this happens,
+	 * the card's reported BSSID will change, but the the new BSS may not
+	 * be in the scan list, since scanning isn't done in ad-hoc mode for
+	 * various reasons.  So pull the BSSID from the card and update the
+	 * current AP with it, if the current AP is adhoc.
+	 */
+	if (priv->current_ap && (nm_ap_get_mode (priv->current_ap) == NM_802_11_MODE_ADHOC)) {
+		struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} };
+
+		nm_device_wifi_get_bssid (self, &bssid);
+		/* 0x02 means "locally administered" and should be OR-ed into
+		 * the first byte of IBSS BSSIDs.
+		 */
+		if (   (bssid.ether_addr_octet[0] & 0x02)
+		    && nm_ethernet_address_is_valid (&bssid))
+			nm_ap_set_address (priv->current_ap, &bssid);
+	}
+
+	new_ap = get_active_ap (self, NULL, FALSE);
+	if (new_ap)
+		nm_device_wifi_update_signal_strength (self, new_ap);
+
+	if ((new_ap || priv->current_ap) && (new_ap != priv->current_ap)) {
+		const struct ether_addr *new_bssid = NULL;
+		const GByteArray *new_ssid = NULL;
+		const struct ether_addr *old_bssid = NULL;
+		const GByteArray *old_ssid = NULL;
+		char *old_addr = NULL, *new_addr = NULL;
+
+		if (new_ap) {
+			new_bssid = nm_ap_get_address (new_ap);
+			new_addr = nm_ether_ntop (new_bssid);
+			new_ssid = nm_ap_get_ssid (new_ap);
+		}
+
+		if (priv->current_ap) {
+			old_bssid = nm_ap_get_address (priv->current_ap);
+			old_addr = nm_ether_ntop (old_bssid);
+			old_ssid = nm_ap_get_ssid (priv->current_ap);
+		}
+
+		nm_log_info (LOGD_WIFI, "(%s): roamed from BSSID %s (%s) to %s (%s)",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             old_addr ? old_addr : "(none)",
+		             old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)",
+		             new_addr ? new_addr : "(none)",
+		             new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)");
+		g_free (old_addr);
+		g_free (new_addr);
+
+		set_current_ap (self, new_ap);
+	}
+
+	new_rate = nm_device_wifi_get_bitrate (self);
+	if (new_rate != priv->rate) {
+		priv->rate = new_rate;
+		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE);
+	}
+}
+
+/*
+ * nm_device_wifi_periodic_update
+ *
+ * Periodically update device statistics.
+ *
+ */
+static gboolean
+nm_device_wifi_periodic_update (gpointer data)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (data);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMDeviceState state;
+
+	/* BSSID and signal strength have meaningful values only if the device
+	   is activated and not scanning */
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (state != NM_DEVICE_STATE_ACTIVATED)
+		goto out;
+
+	if (nm_supplicant_interface_get_scanning (priv->supplicant.iface))
+		goto out;
+
+	periodic_update (self);
+
+out:
+	return TRUE;
+}
+
+static gboolean
+real_hw_is_up (NMDevice *device)
+{
+	return nm_system_device_is_up (device);
+}
+
+static gboolean
+real_hw_bring_up (NMDevice *device, gboolean *no_firmware)
+{
+	if (!NM_DEVICE_WIFI_GET_PRIVATE (device)->enabled)
+		return FALSE;
+
+	return nm_system_device_set_up_down (device, TRUE, no_firmware);
+}
+
+static void
+real_hw_take_down (NMDevice *dev)
+{
+	nm_system_device_set_up_down (dev, FALSE, NULL);
+}
+
+static gboolean
+real_is_up (NMDevice *device)
+{
+	if (!NM_DEVICE_WIFI_GET_PRIVATE (device)->periodic_source_id)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+real_bring_up (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	priv->periodic_source_id = g_timeout_add_seconds (6, nm_device_wifi_periodic_update, self);
+	return TRUE;
+}
+
+static void
+access_point_removed (NMDeviceWifi *device, NMAccessPoint *ap)
+{
+	g_signal_emit (device, signals[ACCESS_POINT_REMOVED], 0, ap);
+}
+
+static void
+remove_all_aps (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	/* Remove outdated APs */
+	while (g_slist_length (priv->ap_list)) {
+		NMAccessPoint *ap = NM_AP (priv->ap_list->data);
+
+		access_point_removed (self, ap);
+		priv->ap_list = g_slist_remove (priv->ap_list, ap);
+		g_object_unref (ap);
+	}
+	g_slist_free (priv->ap_list);
+	priv->ap_list = NULL;
+}
+
+static void
+real_take_down (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (priv->periodic_source_id) {
+		g_source_remove (priv->periodic_source_id);
+		priv->periodic_source_id = 0;
+	}
+
+	cleanup_association_attempt (self, TRUE);
+	set_current_ap (self, NULL);
+	remove_all_aps (self);
+}
+
+static void
+real_deactivate_quickly (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMAccessPoint *orig_ap = nm_device_wifi_get_activation_ap (self);
+
+	cleanup_association_attempt (self, TRUE);
+
+	set_current_ap (self, NULL);
+	priv->rate = 0;
+
+	/* If the AP is 'fake', i.e. it wasn't actually found from
+	 * a scan but the user tried to connect to it manually (maybe it
+	 * was non-broadcasting or something) get rid of it, because 'fake'
+	 * APs should only live for as long as we're connected to them.  Fixes
+	 * a bug where user-created Ad-Hoc APs are never removed from the scan
+	 * list, because scanning is disabled while in Ad-Hoc mode (for stability),
+	 * and thus the AP culling never happens. (bgo #569241)
+	 */
+	if (orig_ap && nm_ap_get_fake (orig_ap)) {
+		access_point_removed (self, orig_ap);
+		priv->ap_list = g_slist_remove (priv->ap_list, orig_ap);
+		g_object_unref (orig_ap);
+	}
+}
+
+static void
+real_deactivate (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+
+	nm_device_wifi_set_mode (self, NM_802_11_MODE_INFRA);
+	/* FIXME: Should we reset the scan interval here? */
+/* 	nm_device_wifi_set_scan_interval (app_data, self, NM_WIRELESS_SCAN_INTERVAL_ACTIVE); */
+}
+
+static gboolean
+real_check_connection_compatible (NMDevice *device,
+                                  NMConnection *connection,
+                                  GError **error)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wireless;
+	const GByteArray *mac;
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME)) {
+		g_set_error (error,
+		             NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS,
+		             "The connection was not a WiFi connection.");
+		return FALSE;
+	}
+
+	s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS));
+	if (!s_wireless) {
+		g_set_error (error,
+		             NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INVALID,
+		             "The connection was not a valid WiFi connection.");
+		return FALSE;
+	}
+
+	mac = nm_setting_wireless_get_mac_address (s_wireless);
+	if (mac && memcmp (mac->data, &(priv->hw_addr.ether_addr_octet), ETH_ALEN)) {
+		g_set_error (error,
+		             NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE,
+		             "The connection's MAC address did not match this device.");
+		return FALSE;
+	}
+
+	// FIXME: check channel/freq/band against bands the hardware supports
+	// FIXME: check encryption against device capabilities
+	// FIXME: check bitrate against device capabilities
+
+	return TRUE;
+}
+
+static gboolean
+real_is_available (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMSupplicantInterface *sup_iface;
+	guint32 state;
+
+	if (!priv->enabled) {
+		nm_log_dbg (LOGD_WIFI, "(%s): not available because not enabled",
+		            nm_device_get_iface (dev));
+		return FALSE;
+	}
+
+	sup_iface = priv->supplicant.iface;
+	if (!sup_iface) {
+		nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant not running",
+		            nm_device_get_iface (dev));
+		return FALSE;
+	}
+
+	state = nm_supplicant_interface_get_state (sup_iface);
+	if (state != NM_SUPPLICANT_INTERFACE_STATE_READY) {
+		nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant interface not ready",
+		            nm_device_get_iface (dev));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static NMConnection *
+real_get_best_auto_connection (NMDevice *dev,
+                               GSList *connections,
+                               char **specific_object)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList *iter, *ap_iter;
+
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		NMConnection *connection = NM_CONNECTION (iter->data);
+		NMSettingConnection *s_con;
+		NMSettingWireless *s_wireless;
+		const GByteArray *mac;
+		NMSettingIP4Config *s_ip4;
+		const char *method = NULL;
+
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+		if (s_con == NULL)
+			continue;
+		if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME))
+			continue;
+		if (!nm_setting_connection_get_autoconnect (s_con))
+			continue;
+
+		s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+		if (!s_wireless)
+			continue;
+
+		mac = nm_setting_wireless_get_mac_address (s_wireless);
+		if (mac && memcmp (mac->data, priv->hw_addr.ether_addr_octet, ETH_ALEN))
+				continue;
+
+		/* Use the connection if it's a shared connection */
+		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+		if (s_ip4)
+			method = nm_setting_ip4_config_get_method (s_ip4);
+
+		if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
+			return connection;
+
+		for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) {
+			NMAccessPoint *ap = NM_AP (ap_iter->data);
+
+			if (nm_ap_check_compatible (ap, connection)) {
+				/* All good; connection is usable */
+				*specific_object = (char *) nm_ap_get_dbus_path (ap);
+				return connection;
+			}
+		}
+	}
+	return NULL;
+}
+
+/*
+ * nm_device_wifi_get_address
+ *
+ * Get a device's hardware address
+ *
+ */
+void
+nm_device_wifi_get_address (NMDeviceWifi *self,
+                            struct ether_addr *addr)
+{
+	NMDeviceWifiPrivate *priv;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (addr != NULL);
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	memcpy (addr, &(priv->hw_addr), sizeof (struct ether_addr));
+}
+
+static void
+nm_device_wifi_ap_list_print (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList * elt;
+	int i = 0;
+
+	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+
+	nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list:");
+	for (elt = priv->ap_list; elt; elt = g_slist_next (elt), i++) {
+		NMAccessPoint * ap = NM_AP (elt->data);
+		nm_ap_print_self (ap, "AP: ");
+	}
+	nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list: done");
+}
+
+static gboolean
+impl_device_get_access_points (NMDeviceWifi *self,
+                               GPtrArray **aps,
+                               GError **err)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList *elt;
+
+	*aps = g_ptr_array_new ();
+
+	for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) {
+		NMAccessPoint * ap = NM_AP (elt->data);
+
+		if (nm_ap_get_ssid (ap))
+			g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (ap)));
+	}
+	return TRUE;
+}
+
+/*
+ * nm_device_get_mode
+ *
+ * Get managed/infrastructure/adhoc mode on a device
+ *
+ */
+NM80211Mode
+nm_device_wifi_get_mode (NMDeviceWifi *self)
+{
+	int fd;
+	NM80211Mode mode = NM_802_11_MODE_UNKNOWN;
+	const char *iface;
+	struct iwreq wrq;
+
+	g_return_val_if_fail (self != NULL, NM_802_11_MODE_UNKNOWN);
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		goto out;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	iface = nm_device_get_iface (NM_DEVICE (self));
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+
+	if (ioctl (fd, SIOCGIWMODE, &wrq) == 0) {
+		switch (wrq.u.mode) {
+		case IW_MODE_ADHOC:
+			mode = NM_802_11_MODE_ADHOC;
+			break;
+		case IW_MODE_INFRA:
+			mode = NM_802_11_MODE_INFRA;
+			break;
+		default:
+			break;
+		}
+	} else {
+		if (errno != ENODEV) {
+			nm_log_warn (LOGD_HW | LOGD_WIFI,
+			             "(%s): error %d getting card mode",
+			             iface, strerror (errno));
+		}
+	}
+	close (fd);
+
+out:
+	return mode;
+}
+
+
+/*
+ * nm_device_wifi_set_mode
+ *
+ * Set managed/infrastructure/adhoc mode on a device
+ *
+ */
+gboolean
+nm_device_wifi_set_mode (NMDeviceWifi *self, const NM80211Mode mode)
+{
+	int fd;
+	const char *iface;
+	gboolean success = FALSE;
+	struct iwreq wrq;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail ((mode == NM_802_11_MODE_INFRA) || (mode == NM_802_11_MODE_ADHOC), FALSE);
+
+	if (nm_device_wifi_get_mode (self) == mode)
+		return TRUE;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		goto out;
+
+	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_INFRA:
+		wrq.u.mode = IW_MODE_INFRA;
+		break;
+	default:
+		goto out;
+	}
+
+	iface = nm_device_get_iface (NM_DEVICE (self));
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+
+	if (ioctl (fd, SIOCSIWMODE, &wrq) < 0) {
+		if (errno != ENODEV) {
+			nm_log_err (LOGD_HW | LOGD_WIFI,
+			            "(%s): error setting mode %d",
+			            iface, mode, strerror (errno));
+		}
+	} else
+		success = TRUE;
+	close (fd);
+
+out:
+	return success;
+}
+
+
+/*
+ * nm_device_wifi_get_frequency
+ *
+ * Get current frequency
+ *
+ */
+static guint32
+nm_device_wifi_get_frequency (NMDeviceWifi *self)
+{
+	int fd;
+	guint32 freq = 0;
+	const char *iface;
+	struct iwreq wrq;
+
+	g_return_val_if_fail (self != NULL, 0);
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return 0;
+
+	memset (&wrq, 0, sizeof (struct iwreq));
+	iface = nm_device_get_iface (NM_DEVICE (self));
+	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
+
+	if (ioctl (fd, SIOCGIWFREQ, &wrq) < 0) {
+		nm_log_warn (LOGD_HW | LOGD_WIFI,
+		             "(%s): error getting frequency: %s",
+		             iface, strerror (errno));
+	} else
+		freq = iw_freq_to_uint32 (&wrq.u.freq);
+
+	close (fd);
+	return freq;
+}
+
+/*
+ * wireless_stats_to_percent
+ *
+ * Convert an iw_stats structure from a scan or the card into
+ * a magical signal strength percentage.
+ *
+ */
+static int
+wireless_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);
+
+	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));
+}
+
+
+/*
+ * nm_device_wifi_get_ssid
+ *
+ * If a device is wireless, return the ssid that it is attempting
+ * to use.
+ */
+const GByteArray *
+nm_device_wifi_get_ssid (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv;
+	int sk;
+	struct iwreq wrq;
+	char ssid[IW_ESSID_MAX_SIZE + 2];
+	guint32 len;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	sk = socket (AF_INET, SOCK_DGRAM, 0);
+	if (!sk) {
+		nm_log_err (LOGD_HW, "couldn't create socket: %d.", errno);
+		return 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, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ);
+
+	if (ioctl (sk, SIOCGIWESSID, &wrq) < 0) {
+		nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): couldn't get SSID: %d",
+		            nm_device_get_iface (NM_DEVICE (self)), errno);
+		goto out;
+	}
+
+	if (priv->ssid) {
+		g_byte_array_free (priv->ssid, TRUE);
+		priv->ssid = NULL;
+	}
+
+	len = wrq.u.essid.length;
+	if (!nm_utils_is_empty_ssid ((guint8 *) ssid, len)) {
+		/* Some drivers include nul termination in the SSID, so let's
+		 * remove it here before further processing. WE-21 changes this
+		 * to explicitly require the length _not_ to include nul
+		 * termination. */
+		if (len > 0 && ssid[len - 1] == '\0' && priv->we_version < 21)
+			len--;
+
+		priv->ssid = g_byte_array_sized_new (len);
+		g_byte_array_append (priv->ssid, (const guint8 *) ssid, len);
+	}
+
+out:
+	close (sk);
+	return priv->ssid;
+}
+
+
+/*
+ * nm_device_wifi_get_bitrate
+ *
+ * For wireless devices, get the bitrate to broadcast/receive at.
+ * Returned value is rate in Kb/s.
+ *
+ */
+static guint32
+nm_device_wifi_get_bitrate (NMDeviceWifi *self)
+{
+	int err = -1, fd;
+	struct iwreq wrq;
+
+	g_return_val_if_fail (self != NULL, 0);
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return 0;
+
+	memset (&wrq, 0, sizeof (wrq));
+	strncpy (wrq.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ);
+	err = ioctl (fd, SIOCGIWRATE, &wrq);
+	close (fd);
+
+	return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0);
+}
+
+/*
+ * nm_device_get_bssid
+ *
+ * If a device is wireless, get the access point's ethernet address
+ * that the card is associated with.
+ */
+void
+nm_device_wifi_get_bssid (NMDeviceWifi *self,
+                          struct ether_addr *bssid)
+{
+	int fd;
+	struct iwreq wrq;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (bssid != NULL);
+
+	memset (bssid, 0, sizeof (struct ether_addr));
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_WIFI, "failed to open control socket.");
+		return;
+	}
+
+	memset (&wrq, 0, sizeof (wrq));
+	strncpy (wrq.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ);
+	if (ioctl (fd, SIOCGIWAP, &wrq) == 0)
+		memcpy (bssid->ether_addr_octet, &(wrq.u.ap_addr.sa_data), ETH_ALEN);
+
+	close (fd);
+}
+
+
+static gboolean
+scanning_allowed (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	guint32 sup_state;
+	NMActRequest *req;
+
+	g_return_val_if_fail (priv->supplicant.iface != NULL, FALSE);
+
+	switch (nm_device_get_state (NM_DEVICE (self))) {
+	case NM_DEVICE_STATE_UNKNOWN:
+	case NM_DEVICE_STATE_UNMANAGED:
+	case NM_DEVICE_STATE_UNAVAILABLE:
+	case NM_DEVICE_STATE_PREPARE:
+	case NM_DEVICE_STATE_CONFIG:
+	case NM_DEVICE_STATE_NEED_AUTH:
+	case NM_DEVICE_STATE_IP_CONFIG:
+		/* Don't scan when unusable or activating */
+		return FALSE;
+	case NM_DEVICE_STATE_DISCONNECTED:
+	case NM_DEVICE_STATE_FAILED:
+		/* Can always scan when disconnected */
+		return TRUE;
+	case NM_DEVICE_STATE_ACTIVATED:
+		/* Need to do further checks when activated */
+		break;
+	}
+
+	/* Don't scan if the supplicant is busy */
+	sup_state = nm_supplicant_interface_get_connection_state (priv->supplicant.iface);
+	if (   sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING
+	    || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED
+	    || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE
+	    || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE
+	    || nm_supplicant_interface_get_scanning (priv->supplicant.iface))
+		return FALSE;
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	if (req) {
+		NMConnection *connection;
+		NMSettingIP4Config *s_ip4;
+		NMSettingWireless *s_wifi;
+		const char *ip4_method = NULL;
+		const GByteArray *bssid;
+
+		/* Don't scan when a shared connection is active; it makes drivers mad */
+		connection = nm_act_request_get_connection (req);
+		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+		if (s_ip4)
+			ip4_method = nm_setting_ip4_config_get_method (s_ip4);
+
+		if (s_ip4 && !strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
+			return FALSE;
+
+		/* Don't scan when the connection is locked to a specifc AP, since
+		 * intra-ESS roaming (which requires periodic scanning) isn't being
+		 * used due to the specific AP lock. (bgo #513820)
+		 */
+		s_wifi = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+		g_assert (s_wifi);
+		bssid = nm_setting_wireless_get_bssid (s_wifi);
+		if (bssid && bssid->len == ETH_ALEN)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+scanning_allowed_accumulator (GSignalInvocationHint *ihint,
+                              GValue *return_accu,
+                              const GValue *handler_return,
+                              gpointer data)
+{
+	if (!g_value_get_boolean (handler_return))
+		g_value_set_boolean (return_accu, FALSE);
+	return TRUE;
+}
+
+static gboolean
+check_scanning_allowed (NMDeviceWifi *self)
+{
+	GValue instance = { 0, };
+	GValue retval = { 0, };
+
+	g_value_init (&instance, G_TYPE_OBJECT);
+	g_value_take_object (&instance, self);
+
+	g_value_init (&retval, G_TYPE_BOOLEAN);
+	g_value_set_boolean (&retval, TRUE);
+
+	/* Use g_signal_emitv() rather than g_signal_emit() to avoid the return
+	 * value being changed if no handlers are connected */
+	g_signal_emitv (&instance, signals[SCANNING_ALLOWED], 0, &retval);
+
+	return g_value_get_boolean (&retval);
+}
+
+static gboolean
+request_wireless_scan (gpointer user_data)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	gboolean backoff = FALSE;
+
+	if (check_scanning_allowed (self)) {
+		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scanning requested",
+		            nm_device_get_iface (NM_DEVICE (self)));
+
+		if (nm_supplicant_interface_request_scan (priv->supplicant.iface)) {
+			/* success */
+			backoff = TRUE;
+		}
+	} else {
+		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan requested but not allowed at this time",
+		            nm_device_get_iface (NM_DEVICE (self)));
+	}
+
+	priv->pending_scan_id = 0;
+	schedule_scan (self, backoff);
+	return FALSE;
+}
+
+
+/*
+ * schedule_scan
+ *
+ * Schedule a wireless scan.
+ *
+ */
+static void
+schedule_scan (NMDeviceWifi *self, gboolean backoff)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GTimeVal now;
+
+	g_get_current_time (&now);
+
+	/* Cancel the pending scan if it would happen later than (now + the scan_interval) */
+	if (priv->pending_scan_id) {
+		if (now.tv_sec + priv->scan_interval < priv->scheduled_scan_time)
+			cancel_pending_scan (self);
+	}
+
+	if (!priv->pending_scan_id) {
+		guint factor = 2, next_scan = priv->scan_interval;
+
+		if (    nm_device_is_activating (NM_DEVICE (self))
+		    || (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED))
+			factor = 1;
+
+		priv->pending_scan_id = g_timeout_add_seconds (next_scan,
+		                                               request_wireless_scan,
+		                                               self);
+
+		priv->scheduled_scan_time = now.tv_sec + priv->scan_interval;
+		if (backoff && (priv->scan_interval < (SCAN_INTERVAL_MAX / factor))) {
+				priv->scan_interval += (SCAN_INTERVAL_STEP / factor);
+				/* Ensure the scan interval will never be less than 20s... */
+				priv->scan_interval = MAX(priv->scan_interval, SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP);
+				/* ... or more than 120s */
+				priv->scan_interval = MIN(priv->scan_interval, SCAN_INTERVAL_MAX);
+		} else if (!backoff && (priv->scan_interval == 0)) {
+			/* Invalid combination; would cause continual rescheduling of
+			 * the scan and hog CPU.  Reset to something minimally sane.
+			 */
+			priv->scan_interval = 5;
+		}
+
+		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scheduled scan in %d seconds (interval now %d seconds)",
+		            nm_device_get_iface (NM_DEVICE (self)),
+		            next_scan,
+		            priv->scan_interval);
+
+	}
+}
+
+
+static void
+cancel_pending_scan (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (priv->pending_scan_id) {
+		g_source_remove (priv->pending_scan_id);
+		priv->pending_scan_id = 0;
+	}
+}
+
+
+static void
+supplicant_iface_scan_request_result_cb (NMSupplicantInterface *iface,
+                                         gboolean success,
+                                         NMDeviceWifi *self)
+{
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan request %s",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            success ? "successful" : "failed");
+
+	if (check_scanning_allowed (self))
+		schedule_scan (self, TRUE);
+}
+
+static void
+supplicant_iface_scan_results_cb (NMSupplicantInterface *iface,
+                                  guint32 num_results,
+                                  NMDeviceWifi *self)
+{
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan results available (%d APs found)",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            num_results);
+	if (num_results == 0) {
+		/* ensure that old APs get culled, which otherwise only
+		 * happens when there are actual scan results to process.
+		 */
+		cull_scan_list (self);
+		nm_device_wifi_ap_list_print (self);
+	}
+}
+
+static gboolean
+is_encrypted (guint32 flags, guint32 wpa_flags, guint32 rsn_flags)
+{
+	if (flags & NM_802_11_AP_FLAGS_PRIVACY)
+		return TRUE;
+	if (wpa_flags & (NM_802_11_AP_SEC_KEY_MGMT_PSK | NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+		return TRUE;
+	if (rsn_flags & (NM_802_11_AP_SEC_KEY_MGMT_PSK | NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+		return TRUE;
+
+	return FALSE;
+}
+
+/*
+ * ap_auth_enforced
+ *
+ * Checks whether or not there is an encryption key present for
+ * this connection, and whether or not the authentication method
+ * in use will result in an authentication rejection if the key
+ * is wrong.  For example, Ad Hoc mode networks don't have a
+ * master node and therefore nothing exists to reject the station.
+ * Similarly, Open System WEP access points don't reject a station
+ * when the key is wrong.  Shared Key WEP access points will.
+ *
+ */
+static gboolean
+ap_auth_enforced (NMConnection *connection,
+                  NMAccessPoint *ap,
+                  gboolean *encrypted)
+{
+	guint32 flags, wpa_flags, rsn_flags;
+	gboolean enforced = FALSE;
+
+	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (encrypted != NULL, FALSE);
+
+	flags = nm_ap_get_flags (ap);
+	wpa_flags = nm_ap_get_wpa_flags (ap);
+	rsn_flags = nm_ap_get_rsn_flags (ap);
+
+	if (nm_ap_get_mode (ap) == NM_802_11_MODE_ADHOC)
+		goto out;
+
+	/* Static WEP */
+	if (   (flags & NM_802_11_AP_FLAGS_PRIVACY)
+        && (wpa_flags == NM_802_11_AP_SEC_NONE)
+        && (rsn_flags == NM_802_11_AP_SEC_NONE)) {
+		NMSettingWirelessSecurity *s_wireless_sec;
+		const char *auth_alg;
+
+		/* No way to tell if the key is wrong with Open System
+		 * auth mode in WEP.  Auth is not enforced like Shared Key.
+		 */
+		s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, 
+																    NM_TYPE_SETTING_WIRELESS_SECURITY);
+		if (s_wireless_sec) {
+			auth_alg = nm_setting_wireless_security_get_auth_alg (s_wireless_sec);
+			if (!auth_alg || !strcmp (auth_alg, "open"))
+				goto out;
+		}
+
+		enforced = TRUE;
+	} else if (wpa_flags != NM_802_11_AP_SEC_NONE) { /* WPA */
+		enforced = TRUE;
+	} else if (rsn_flags != NM_802_11_AP_SEC_NONE) { /* WPA2 */
+		enforced = TRUE;
+	}
+
+out:
+	*encrypted = is_encrypted (flags, wpa_flags, rsn_flags);
+	return enforced;
+}
+
+
+/****************************************************************************
+ * WPA Supplicant control stuff
+ *
+ */
+
+/*
+ * merge_scanned_ap
+ *
+ * If there is already an entry that matches the BSSID and ESSID of the
+ * AP to merge, replace that entry with the scanned AP.  Otherwise, add
+ * the scanned AP to the list.
+ *
+ * TODO: possibly need to differentiate entries based on security too; i.e. if
+ * there are two scan results with the same BSSID and SSID but different
+ * security options?
+ *
+ */
+static void
+merge_scanned_ap (NMDeviceWifi *self,
+                  NMAccessPoint *merge_ap)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMAccessPoint *found_ap = NULL;
+	const GByteArray *ssid;
+	gboolean strict_match = TRUE;
+	NMAccessPoint *current_ap = NULL;
+
+	/* Let the manager try to fill in the SSID from seen-bssids lists
+	 * if it can
+	 */
+	ssid = nm_ap_get_ssid (merge_ap);
+	if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
+		g_signal_emit (self, signals[HIDDEN_AP_FOUND], 0, merge_ap);
+		nm_ap_set_broadcast (merge_ap, FALSE);
+	}
+
+	/* If the incoming scan result matches the hidden AP that NM is currently
+	 * connected to but hasn't been seen in the scan list yet, don't use
+	 * strict matching.  Because the capabilities of the fake AP have to be
+	 * constructed from the NMConnection of the activation request, they won't
+	 * always be the same as the capabilities of the real AP from the scan.
+	 */
+	current_ap = nm_device_wifi_get_activation_ap (self);
+	if (current_ap && nm_ap_get_fake (current_ap))
+		strict_match = FALSE;
+
+	found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match);
+	if (found_ap) {
+		nm_ap_set_flags (found_ap, nm_ap_get_flags (merge_ap));
+		nm_ap_set_wpa_flags (found_ap, nm_ap_get_wpa_flags (merge_ap));
+		nm_ap_set_rsn_flags (found_ap, nm_ap_get_rsn_flags (merge_ap));
+		nm_ap_set_strength (found_ap, nm_ap_get_strength (merge_ap));
+		nm_ap_set_last_seen (found_ap, nm_ap_get_last_seen (merge_ap));
+		nm_ap_set_broadcast (found_ap, nm_ap_get_broadcast (merge_ap));
+		nm_ap_set_freq (found_ap, nm_ap_get_freq (merge_ap));
+		nm_ap_set_max_bitrate (found_ap, nm_ap_get_max_bitrate (merge_ap));
+
+		/* If the AP is noticed in a scan, it's automatically no longer
+		 * fake, since it clearly exists somewhere.
+		 */
+		nm_ap_set_fake (found_ap, FALSE);
+	} else {
+		/* New entry in the list */
+		// FIXME: figure out if reference counts are correct here for AP objects
+		g_object_ref (merge_ap);
+		priv->ap_list = g_slist_prepend (priv->ap_list, merge_ap);
+		nm_ap_export_to_dbus (merge_ap);
+		g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, merge_ap);
+	}
+}
+
+static void
+cull_scan_list (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv;
+	GTimeVal cur_time;
+	GSList *outdated_list = NULL;
+	GSList *elt;
+	NMActRequest *req;
+	const char *cur_ap_path = NULL;
+	guint32 removed = 0, total = 0;
+
+	g_return_if_fail (self != NULL);
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	g_get_current_time (&cur_time);
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	if (req)
+		cur_ap_path = nm_act_request_get_specific_object (req);
+
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): checking scan list for outdated APs",
+	            nm_device_get_iface (NM_DEVICE (self)));
+
+	/* Walk the access point list and remove any access points older than
+	 * three times the inactive scan interval.
+	 */
+	for (elt = priv->ap_list; elt; elt = g_slist_next (elt), total++) {
+		NMAccessPoint * ap = NM_AP (elt->data);
+		const glong     ap_time = nm_ap_get_last_seen (ap);
+		gboolean        keep = FALSE;
+		const guint     prune_interval_s = SCAN_INTERVAL_MAX * 3;
+
+		/* Don't ever prune the AP we're currently associated with */
+		if (cur_ap_path && !strcmp (cur_ap_path, nm_ap_get_dbus_path (ap)))
+			keep = TRUE;
+		if (nm_ap_get_fake (ap))
+			keep = TRUE;
+
+		if (!keep && (ap_time + prune_interval_s < cur_time.tv_sec))
+			outdated_list = g_slist_append (outdated_list, ap);
+	}
+
+	/* Remove outdated APs */
+	for (elt = outdated_list; elt; elt = g_slist_next (elt)) {
+		NMAccessPoint *outdated_ap = NM_AP (elt->data);
+		const struct ether_addr *bssid;
+		const GByteArray *ssid;
+
+		bssid = nm_ap_get_address (outdated_ap);
+		ssid = nm_ap_get_ssid (outdated_ap);
+		nm_log_dbg (LOGD_WIFI_SCAN,
+		            "   removing %02x:%02x:%02x:%02x:%02x:%02x (%s%s%s)",
+		            bssid->ether_addr_octet[0], bssid->ether_addr_octet[1],
+		            bssid->ether_addr_octet[2], bssid->ether_addr_octet[3],
+		            bssid->ether_addr_octet[4], bssid->ether_addr_octet[5],
+		            ssid ? "'" : "",
+		            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
+		            ssid ? "'" : "");
+
+		access_point_removed (self, outdated_ap);
+		priv->ap_list = g_slist_remove (priv->ap_list, outdated_ap);
+		g_object_unref (outdated_ap);
+		removed++;
+	}
+	g_slist_free (outdated_list);
+
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): removed %d APs (of %d)",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            removed, total);
+}
+
+#define SET_QUALITY_MEMBER(qual_item, lc_member, uc_member) \
+	if (lc_member != -1) { \
+		qual_item.lc_member = lc_member; \
+		qual_item.updated |= IW_QUAL_##uc_member##_UPDATED; \
+	} else { \
+		qual_item.updated |= IW_QUAL_##uc_member##_INVALID; \
+	}
+
+static void
+set_ap_strength_from_properties (NMDeviceWifi *self,
+                                 NMAccessPoint *ap,
+                                 GHashTable *properties)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	int qual, level, noise;
+	struct iw_quality quality;
+	GValue *value;
+	gint8 strength;
+
+	value = (GValue *) g_hash_table_lookup (properties, "quality");
+	qual = value ? g_value_get_int (value) : -1;
+
+	value = (GValue *) g_hash_table_lookup (properties, "level");
+	level = value ? g_value_get_int (value) : -1;
+
+	value = (GValue *) g_hash_table_lookup (properties, "noise");
+	noise = value ? g_value_get_int (value) : -1;
+
+	/* Calculate and set the AP's signal quality */
+	memset (&quality, 0, sizeof (struct iw_quality));
+	SET_QUALITY_MEMBER (quality, qual, QUAL);
+	SET_QUALITY_MEMBER (quality, level, LEVEL);
+	SET_QUALITY_MEMBER (quality, noise, NOISE);
+
+	strength = wireless_qual_to_percent (&quality, &priv->max_qual);
+	nm_ap_set_strength (ap, strength);
+}
+
+static void
+supplicant_iface_scanned_ap_cb (NMSupplicantInterface *iface,
+                                GHashTable *properties,
+                                NMDeviceWifi *self)
+{
+	NMDeviceState state;
+	NMAccessPoint *ap;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (properties != NULL);
+	g_return_if_fail (iface != NULL);
+
+	/* Ignore new APs when unavailable or unamnaged */
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
+		return;
+
+	ap = nm_ap_new_from_properties (properties);
+	if (ap) {
+		set_ap_strength_from_properties (self, ap, properties);
+
+		nm_ap_print_self (ap, "AP: ");
+
+		/* Add the AP to the device's AP list */
+		merge_scanned_ap (self, ap);
+		g_object_unref (ap);
+
+		/* Remove outdated access points */
+		cull_scan_list (self);
+
+		nm_device_wifi_ap_list_print (self);
+	} else {
+		nm_log_warn (LOGD_WIFI_SCAN, "(%s): invalid AP properties received",
+		             nm_device_get_iface (NM_DEVICE (self)));
+	}
+}
+
+
+static void
+cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	remove_supplicant_interface_error_handler (self);
+	remove_supplicant_timeouts (self);
+	if (disconnect && priv->supplicant.iface)
+		nm_supplicant_interface_disconnect (priv->supplicant.iface);
+}
+
+
+static void
+remove_link_timeout (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv;
+
+	g_return_if_fail (self != NULL);
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (priv->link_timeout_id) {
+		g_source_remove (priv->link_timeout_id);
+		priv->link_timeout_id = 0;
+	}
+}
+
+
+/*
+ * link_timeout_cb
+ *
+ * Called when the link to the access point has been down for a specified
+ * period of time.
+ */
+static gboolean
+link_timeout_cb (gpointer user_data)
+{
+	NMDevice *              dev = NM_DEVICE (user_data);
+	NMDeviceWifi * self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMActRequest *          req = NULL;
+	NMAccessPoint *         ap = NULL;
+	NMConnection *          connection;
+	const char *            setting_name;
+	gboolean                auth_enforced, encrypted = FALSE;
+
+	g_assert (dev);
+
+	priv->link_timeout_id = 0;
+
+	req = nm_device_get_act_request (dev);
+	ap = nm_device_wifi_get_activation_ap (self);
+	if (req == NULL || ap == NULL) {
+		/* shouldn't ever happen */
+		nm_log_err (LOGD_WIFI, "couldn't get activation request or activation AP.");
+		if (nm_device_is_activating (dev)) {
+			cleanup_association_attempt (self, TRUE);
+			nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE);
+		}
+		return FALSE;
+	}
+
+	/* Disconnect event while activated; the supplicant hasn't been able
+	 * to reassociate within the timeout period, so the connection must
+	 * fail.
+	 */
+	if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) {
+		nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT);
+		return FALSE;
+	}
+
+	/* Disconnect event during initial authentication and credentials
+	 * ARE checked - we are likely to have wrong key.  Ask the user for
+	 * another one.
+	 */
+	if (nm_device_get_state (dev) != NM_DEVICE_STATE_CONFIG)
+		goto time_out;
+
+	connection = nm_act_request_get_connection (req);
+	if (!connection)
+		goto time_out;
+
+	auth_enforced = ap_auth_enforced (connection, ap, &encrypted);
+	if (!encrypted || !auth_enforced)
+		goto time_out;
+
+	/* Drivers are still just too crappy, and emit too many disassociation
+	 * events during connection.  So for now, just let the driver and supplicant
+	 * keep trying to associate, and don't ask for new secrets when we get
+	 * disconnected during association.
+	 */
+	if (0) {
+		nm_connection_clear_secrets (connection);
+		setting_name = nm_connection_need_secrets (connection, NULL);
+		if (!setting_name)
+			goto time_out;
+
+		/* Association/authentication failed during association, probably have a 
+		 * bad encryption key and the authenticating entity (AP, RADIUS server, etc)
+		 * denied the association due to bad credentials.
+		 */
+		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): disconnected during association,"
+		             " asking for new key.", nm_device_get_iface (dev));
+		cleanup_association_attempt (self, TRUE);
+		nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+		nm_act_request_get_secrets (req,
+		                            setting_name,
+		                            TRUE,
+		                            SECRETS_CALLER_WIFI,
+		                            NULL,
+		                            NULL);
+
+		return FALSE;
+	}
+
+time_out:
+	nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev));
+	return FALSE;
+}
+
+static gboolean
+schedule_state_handler (NMDeviceWifi *self,
+                        GSourceFunc handler,
+                        guint32 new_state,
+                        guint32 old_state,
+                        gboolean mgr_task)
+{
+	NMDeviceWifiPrivate *priv;
+	SupplicantStateTask *task;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (handler != NULL, FALSE);
+
+	if (new_state == old_state)
+		return TRUE;
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	task = g_slice_new0 (SupplicantStateTask);
+	if (!task) {
+		nm_log_err (LOGD_WIFI, "Not enough memory to process supplicant manager state change.");
+		return FALSE;
+	}
+
+	task->self = self;
+	task->new_state = new_state;
+	task->old_state = old_state;
+	task->mgr_task = mgr_task;
+
+	task->source_id = g_idle_add (handler, task);
+	if (mgr_task)
+		priv->supplicant.mgr_tasks = g_slist_append (priv->supplicant.mgr_tasks, task);
+	else
+		priv->supplicant.iface_tasks = g_slist_append (priv->supplicant.iface_tasks, task);
+
+	return TRUE;
+}
+
+static gboolean
+supplicant_iface_state_cb_handler (gpointer user_data)
+{
+	SupplicantStateTask *task = (SupplicantStateTask *) user_data;
+	NMDeviceWifi *self;
+	NMDeviceWifiPrivate *priv;
+
+	g_return_val_if_fail (task != NULL, FALSE);
+
+	self = task->self;
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	nm_log_info (LOGD_WIFI, "(%s): supplicant interface state:  %s -> %s",
+	             nm_device_get_iface (NM_DEVICE (self)),
+	             nm_supplicant_interface_state_to_string (task->old_state),
+	             nm_supplicant_interface_state_to_string (task->new_state));
+
+	if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) {
+		priv->scan_interval = SCAN_INTERVAL_MIN;
+
+		/* If the interface can now be activated because the supplicant is now
+		 * available, transition to DISCONNECTED.
+		 */
+		if (   (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_UNAVAILABLE)
+		    && nm_device_is_available (NM_DEVICE (self))) {
+			nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED,
+			                         NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE);
+		}
+
+		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): supplicant ready, requesting initial scan",
+		            nm_device_get_iface (NM_DEVICE (self)));
+
+		/* Request a scan to get latest results */
+		cancel_pending_scan (self);
+		request_wireless_scan (self);
+	} else if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
+		cleanup_association_attempt (self, FALSE);
+		supplicant_interface_release (self);
+		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE,
+		                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+	}
+
+	finish_supplicant_task (task, FALSE);
+	return FALSE;
+}
+
+
+static void
+supplicant_iface_state_cb (NMSupplicantInterface * iface,
+                           guint32 new_state,
+                           guint32 old_state,
+                           NMDeviceWifi *self)
+{
+	g_return_if_fail (self != NULL);
+
+	schedule_state_handler (self,
+	                        supplicant_iface_state_cb_handler,
+	                        new_state,
+	                        old_state,
+	                        FALSE);
+}
+
+
+static gboolean
+supplicant_iface_connection_state_cb_handler (gpointer user_data)
+{
+	SupplicantStateTask *task = (SupplicantStateTask *) user_data;
+	NMDeviceWifi *self = task->self;
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMDevice *dev = NM_DEVICE (self);
+	gboolean scanning;
+
+	if (!nm_device_get_act_request (dev)) {
+		/* The device is not activating or already activated; do nothing. */
+		goto out;
+	}
+
+	nm_log_info (LOGD_WIFI, "(%s): supplicant connection state:  %s -> %s",
+	             nm_device_get_iface (dev),
+	             nm_supplicant_interface_connection_state_to_string (task->old_state),
+	             nm_supplicant_interface_connection_state_to_string (task->new_state));
+
+	scanning = nm_supplicant_interface_get_scanning (priv->supplicant.iface);
+
+	if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) {
+		remove_supplicant_interface_error_handler (self);
+		remove_supplicant_timeouts (self);
+
+		/* If this is the initial association during device activation,
+		 * schedule the next activation stage.
+		 */
+		if (nm_device_get_state (dev) == NM_DEVICE_STATE_CONFIG) {
+			NMAccessPoint *ap = nm_device_wifi_get_activation_ap (self);
+			const GByteArray * ssid = nm_ap_get_ssid (ap);
+
+			nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+			             "Activation (%s/wireless) Stage 2 of 5 (Device Configure) "
+			             "successful.  Connected to wireless network '%s'.",
+			             nm_device_get_iface (dev),
+			             ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)");
+			nm_device_activate_schedule_stage3_ip_config_start (dev);
+		}
+	} else if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) {
+		if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (dev)) {
+			/* Start the link timeout so we allow some time for reauthentication,
+			 * use a longer timeout if we are scanning since some cards take a
+			 * while to scan.
+			 */
+			if (!priv->link_timeout_id) {
+				priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15,
+				                                               link_timeout_cb, self);
+			}
+		}
+	}
+
+out:
+	finish_supplicant_task (task, FALSE);
+	return FALSE;
+}
+
+
+static void
+supplicant_iface_connection_state_cb (NMSupplicantInterface * iface,
+                                      guint32 new_state,
+                                      guint32 old_state,
+                                      NMDeviceWifi *self)
+{
+	g_return_if_fail (self != NULL);
+
+	schedule_state_handler (self,
+	                        supplicant_iface_connection_state_cb_handler,
+	                        new_state,
+	                        old_state,
+	                        FALSE);
+}
+
+
+static gboolean
+supplicant_mgr_state_cb_handler (gpointer user_data)
+{
+	SupplicantStateTask *task = (SupplicantStateTask *) user_data;
+	NMDeviceWifi *self;
+	NMDeviceWifiPrivate *priv;
+	NMDevice *dev;
+	NMDeviceState dev_state;
+
+	g_return_val_if_fail (task != NULL, FALSE);
+
+	self = task->self;
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	dev = NM_DEVICE (self);
+
+	nm_log_info (LOGD_WIFI, "(%s): supplicant manager state:  %s -> %s",
+	             nm_device_get_iface (NM_DEVICE (self)),
+	             nm_supplicant_manager_state_to_string (task->old_state),
+	             nm_supplicant_manager_state_to_string (task->new_state));
+
+	/* If the supplicant went away, release the supplicant interface */
+	if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) {
+		if (priv->supplicant.iface) {
+			cleanup_association_attempt (self, FALSE);
+			supplicant_interface_release (self);
+		}
+
+		if (nm_device_get_state (dev) > NM_DEVICE_STATE_UNAVAILABLE) {
+			nm_device_state_changed (dev, NM_DEVICE_STATE_UNAVAILABLE,
+			                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		}
+	} else if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_IDLE) {
+		dev_state = nm_device_get_state (dev);
+		if (    priv->enabled
+		    && !priv->supplicant.iface
+		    && (dev_state >= NM_DEVICE_STATE_UNAVAILABLE)) {
+			/* request a supplicant interface from the supplicant manager */
+			supplicant_interface_acquire (self);
+
+			/* if wireless is enabled and we have a supplicant interface,
+			 * we can transition to the DISCONNECTED state.
+			 */
+			if (priv->supplicant.iface) {
+				nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED,
+				                         NM_DEVICE_STATE_REASON_NONE);
+			}
+		}
+	}
+
+	finish_supplicant_task (task, FALSE);
+	return FALSE;
+}
+
+static void
+supplicant_mgr_state_cb (NMSupplicantInterface * iface,
+                         guint32 new_state,
+                         guint32 old_state,
+                         NMDeviceWifi *self)
+{
+	g_return_if_fail (self != NULL);
+
+	schedule_state_handler (self,
+	                        supplicant_mgr_state_cb_handler,
+	                        new_state,
+	                        old_state,
+	                        TRUE);
+}
+
+struct iface_con_error_cb_data {
+	NMDeviceWifi *self;
+	char *name;
+	char *message;
+};
+
+static gboolean
+supplicant_iface_connection_error_cb_handler (gpointer user_data)
+{
+	NMDeviceWifi *self;
+	NMDeviceWifiPrivate *priv;
+	struct iface_con_error_cb_data * cb_data = (struct iface_con_error_cb_data *) user_data;
+
+	g_return_val_if_fail (cb_data != NULL, FALSE);
+
+	self = cb_data->self;
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (!nm_device_is_activating (NM_DEVICE (self)))
+		goto out;
+
+	nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+	             "Activation (%s/wireless): association request to the supplicant "
+	             "failed: %s - %s",
+	             nm_device_get_iface (NM_DEVICE (self)),
+	             cb_data->name,
+	             cb_data->message);
+
+	cleanup_association_attempt (self, TRUE);
+	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+
+out:
+	priv->supplicant.iface_con_error_cb_id = 0;
+	g_free (cb_data->name);
+	g_free (cb_data->message);
+	g_slice_free (struct iface_con_error_cb_data, cb_data);
+	return FALSE;
+}
+
+
+static void
+supplicant_iface_connection_error_cb (NMSupplicantInterface * iface,
+                                      const char * name,
+                                      const char * message,
+                                      NMDeviceWifi * self)
+{
+	NMDeviceWifiPrivate *priv;
+	struct iface_con_error_cb_data *cb_data;
+	guint id;
+
+	g_return_if_fail (self != NULL);
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	cb_data = g_slice_new0 (struct iface_con_error_cb_data);
+	if (cb_data == NULL) {
+		nm_log_err (LOGD_WIFI, "Not enough memory to process supplicant connection error.");
+		return;
+	}
+
+	cb_data->self = self;
+	cb_data->name = g_strdup (name);
+	cb_data->message = g_strdup (message);
+
+	if (priv->supplicant.iface_con_error_cb_id)
+		g_source_remove (priv->supplicant.iface_con_error_cb_id);
+
+	id = g_idle_add (supplicant_iface_connection_error_cb_handler, cb_data);
+	priv->supplicant.iface_con_error_cb_id = id;
+}
+
+static void
+supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
+                                     GParamSpec *pspec,
+                                     NMDeviceWifi *self)
+{
+	gboolean scanning;
+
+	scanning = nm_supplicant_interface_get_scanning (iface);
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): now %s",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            scanning ? "scanning" : "not scanning");
+
+	g_object_notify (G_OBJECT (self), "scanning");
+}
+
+static void
+remove_supplicant_connection_timeout (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv;
+
+	g_return_if_fail (self != NULL);
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	/* Remove any pending timeouts on the request */
+	if (priv->supplicant.con_timeout_id) {
+		g_source_remove (priv->supplicant.con_timeout_id);
+		priv->supplicant.con_timeout_id = 0;
+	}
+}
+
+static NMActStageReturn
+handle_auth_or_fail (NMDeviceWifi *self,
+                     NMActRequest *req,
+                     gboolean new_secrets)
+{
+	const char *setting_name;
+	guint32 tries;
+	NMAccessPoint *ap;
+	NMConnection *connection;
+
+	g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NM_ACT_STAGE_RETURN_FAILURE);
+
+	if (!req) {
+		req = nm_device_get_act_request (NM_DEVICE (self));
+		g_assert (req);
+	}
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	ap = nm_device_wifi_get_activation_ap (self);
+	g_assert (ap);
+
+	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES));
+	if (tries > 3)
+		return NM_ACT_STAGE_RETURN_FAILURE;
+
+	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
+
+	nm_connection_clear_secrets (connection);
+	setting_name = nm_connection_need_secrets (connection, NULL);
+	if (setting_name) {
+		gboolean get_new;
+
+		/* If the caller doesn't necessarily want completely new secrets,
+		 * only ask for new secrets after the first failure.
+		 */
+		get_new = new_secrets ? TRUE : (tries ? TRUE : FALSE);
+		nm_act_request_get_secrets (req,
+		                            setting_name,
+		                            get_new,
+		                            SECRETS_CALLER_WIFI,
+		                            NULL,
+		                            NULL);
+
+		g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
+	} else {
+		nm_log_warn (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
+	}
+	return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+/*
+ * supplicant_connection_timeout_cb
+ *
+ * Called when the supplicant has been unable to connect to an access point
+ * within a specified period of time.
+ */
+static gboolean
+supplicant_connection_timeout_cb (gpointer user_data)
+{
+	NMDevice *              dev = NM_DEVICE (user_data);
+	NMDeviceWifi * self = NM_DEVICE_WIFI (user_data);
+	NMAccessPoint *         ap;
+	NMActRequest *          req;
+	gboolean                auth_enforced = FALSE, encrypted = FALSE;
+	NMConnection *connection;
+
+	cleanup_association_attempt (self, TRUE);
+
+	if (!nm_device_is_activating (dev))
+		return FALSE;
+
+	/* Timed out waiting for authentication success; if the security in use
+	 * does not require access point side authentication (Open System
+	 * WEP, for example) then we are likely using the wrong authentication
+	 * algorithm or key.  Request new one from the user.
+	 */
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	ap = nm_device_wifi_get_activation_ap (self);
+	g_assert (ap);
+
+	auth_enforced = ap_auth_enforced (connection, ap, &encrypted);
+	if (!encrypted) {
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): association took too long, "
+		             "failing activation.",
+		             nm_device_get_iface (dev));
+		nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT);
+	} else {
+		/* Authentication failed, encryption key is probably bad */
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): association took too long.",
+		             nm_device_get_iface (dev));
+
+		if (handle_auth_or_fail (self, req, TRUE) == NM_ACT_STAGE_RETURN_POSTPONE) {
+			nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+			             "Activation (%s/wireless): asking for new secrets",
+			             nm_device_get_iface (dev));
+		} else {
+			nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_NO_SECRETS);
+		}
+	}
+
+	return FALSE;
+}
+
+
+static gboolean
+start_supplicant_connection_timeout (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv;
+	guint id;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	/* Set up a timeout on the connection attempt to fail it after 25 seconds */
+	id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
+	if (id == 0) {
+		nm_log_err (LOGD_DEVICE | LOGD_WIFI,
+		            "Activation (%s/wireless): couldn't start supplicant "
+		            "timeout timer.",
+		            nm_device_get_iface (NM_DEVICE (self)));
+		return FALSE;
+	}
+	priv->supplicant.con_timeout_id = id;
+	return TRUE;
+}
+
+
+static void
+remove_supplicant_timeouts (NMDeviceWifi *self)
+{
+	g_return_if_fail (self != NULL);
+
+	remove_supplicant_connection_timeout (self);
+	remove_link_timeout (self);
+}
+
+static guint32
+find_supported_frequency (NMDeviceWifi *self, guint32 *freqs)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	int i;
+
+	for (i = 0; i < priv->num_freqs; i++) {
+		while (*freqs) {
+			if (priv->freqs[i] == *freqs)
+				return *freqs;
+			freqs++;
+		}
+	}
+
+	return 0;
+}
+
+static NMSupplicantConfig *
+build_supplicant_config (NMDeviceWifi *self,
+                         NMConnection *connection,
+                         NMAccessPoint *ap)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMSupplicantConfig *config = NULL;
+	NMSettingWireless *s_wireless;
+	NMSettingWirelessSecurity *s_wireless_sec;
+	guint32 adhoc_freq = 0;
+
+	g_return_val_if_fail (self != NULL, NULL);
+
+	s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+	g_return_val_if_fail (s_wireless != NULL, NULL);
+
+	config = nm_supplicant_config_new ();
+	if (!config)
+		return NULL;
+
+	/* Figure out the Ad-Hoc frequency to use if creating an adhoc network; if
+	 * nothing was specified then pick something usable.
+	 */
+	if ((nm_ap_get_mode (ap) == NM_802_11_MODE_ADHOC) && nm_ap_get_user_created (ap)) {
+		const char *band = nm_setting_wireless_get_band (s_wireless);
+
+		adhoc_freq = nm_ap_get_freq (ap);
+		if (!adhoc_freq) {
+			if (band && !strcmp (band, "a")) {
+				guint32 a_freqs[] = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0};
+				adhoc_freq = find_supported_frequency (self, a_freqs);
+			} else {
+				guint32 bg_freqs[] = {2412, 2437, 2462, 2472, 0};
+				adhoc_freq = find_supported_frequency (self, bg_freqs);
+			}
+		}
+
+		if (!adhoc_freq) {
+			if (band && !strcmp (band, "a"))
+				adhoc_freq = 5180;
+			else
+				adhoc_freq = 2462;
+		}
+	}
+
+	if (!nm_supplicant_config_add_setting_wireless (config,
+	                                                s_wireless,
+	                                                nm_ap_get_broadcast (ap),
+	                                                adhoc_freq,
+	                                                priv->has_scan_capa_ssid)) {
+		nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config.");
+		goto error;
+	}
+
+	s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
+	if (s_wireless_sec) {
+		NMSetting8021x *s_8021x;
+		const char *con_path = nm_connection_get_path (connection);
+
+		g_assert (con_path);
+		s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+		if (!nm_supplicant_config_add_setting_wireless_security (config,
+		                                                         s_wireless_sec,
+		                                                         s_8021x,
+		                                                         con_path)) {
+			nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless-security setting to "
+			            "supplicant config.");
+			goto error;
+		}
+	} else {
+		if (!nm_supplicant_config_add_no_security (config)) {
+			nm_log_err (LOGD_WIFI, "Couldn't add unsecured option to supplicant config.");
+			goto error;
+		}
+	}
+
+	return config;
+
+error:
+	g_object_unref (config);
+	return NULL;
+}
+
+/****************************************************************************/
+
+static void
+real_update_hw_address (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	struct ifreq req;
+	int fd;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_HW, "could not open control socket.");
+		return;
+	}
+
+	memset (&req, 0, sizeof (struct ifreq));
+	strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+	if (ioctl (fd, SIOCGIFHWADDR, &req) < 0) {
+		nm_log_err (LOGD_HW | LOGD_WIFI, "(%s) error getting hardware address: %d",
+		            nm_device_get_iface (dev), errno);
+		goto out;
+	}
+
+	if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) {
+		memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr));
+		g_object_notify (G_OBJECT (dev), NM_DEVICE_WIFI_HW_ADDRESS);
+	}
+
+out:
+	close (fd);
+}
+
+
+static NMActStageReturn
+real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMAccessPoint *ap = NULL;
+	NMActRequest *req;
+	NMConnection *connection;
+	GSList *iter;
+
+	/* If the user is trying to connect to an AP that NM doesn't yet know about
+	 * (hidden network or something), create an fake AP from the security
+	 * settings in the connection to use until the AP is recognized from the
+	 * scan list, which should show up when the connection is successful.
+	 */
+	ap = nm_device_wifi_get_activation_ap (self);
+	if (ap)
+		goto done;
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	connection = nm_act_request_get_connection (req);
+	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	/* Find a compatible AP in the scan list */
+	for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
+		NMAccessPoint *candidate = NM_AP (iter->data);
+
+		if (nm_ap_check_compatible (candidate, connection)) {
+			ap = candidate;
+			break;
+		}
+	}
+
+	/* If no compatible AP was found, create a fake AP (network is likely
+	 * hidden) and try to use that.
+	 */
+	if (!ap) {
+		ap = nm_ap_new_fake_from_connection (connection);
+		g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+		switch (nm_ap_get_mode (ap)) {
+			case NM_802_11_MODE_ADHOC:
+				nm_ap_set_user_created (ap, TRUE);
+				break;
+			case NM_802_11_MODE_INFRA:
+			default:
+				nm_ap_set_broadcast (ap, FALSE);
+				break;
+		}
+
+		priv->ap_list = g_slist_prepend (priv->ap_list, ap);
+		nm_ap_export_to_dbus (ap);
+		g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, ap);
+	}
+
+	nm_act_request_set_specific_object (req, nm_ap_get_dbus_path (ap));
+
+done:
+	set_current_ap (self, ap);
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+
+static void
+real_connection_secrets_updated (NMDevice *dev,
+                                 NMConnection *connection,
+                                 GSList *updated_settings,
+                                 RequestSecretsCaller caller)
+{
+	NMActRequest *req;
+	gboolean valid = FALSE;
+	GSList *iter;
+
+	g_return_if_fail (caller == SECRETS_CALLER_WIFI);
+
+	if (nm_device_get_state (dev) != NM_DEVICE_STATE_NEED_AUTH)
+		return;
+
+	for (iter = updated_settings; iter; iter = g_slist_next (iter)) {
+		const char *setting_name = (const char *) iter->data;
+
+		if (   !strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME)
+		    || !strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME)) {
+			valid = TRUE;
+		} else {
+			nm_log_warn (LOGD_DEVICE, "Ignoring updated secrets for setting '%s'.",
+			             setting_name);
+		}
+	}
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	g_return_if_fail (nm_act_request_get_connection (req) == connection);
+
+	nm_device_activate_schedule_stage1_device_prepare (dev);
+}
+
+static NMActStageReturn
+real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	const char *iface = nm_device_get_iface (dev);
+	NMSupplicantConfig *config = NULL;
+	gulong id = 0;
+	NMActRequest *req;
+	NMAccessPoint *ap;
+	NMConnection *connection;
+	NMSettingConnection *s_connection;
+	const char *setting_name;
+	NMSettingWireless *s_wireless;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	remove_supplicant_timeouts (self);
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	ap = nm_device_wifi_get_activation_ap (self);
+	g_assert (ap);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	s_connection = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_connection);
+
+	s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+	g_assert (s_wireless);
+
+	/* If we need secrets, get them */
+	setting_name = nm_connection_need_secrets (connection, NULL);
+	if (setting_name) {
+		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): access point '%s' has security,"
+		             " but secrets are required.",
+		             iface, nm_setting_connection_get_id (s_connection));
+
+		ret = handle_auth_or_fail (self, req, FALSE);
+		if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+		goto out;
+	}
+
+	/* have secrets, or no secrets required */
+	if (nm_setting_wireless_get_security (s_wireless)) {
+		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): connection '%s' has security"
+		             ", and secrets exist.  No new secrets needed.",
+		             iface, nm_setting_connection_get_id (s_connection));
+	} else {
+		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): connection '%s' requires no "
+		             "security.  No secrets needed.",
+		             iface, nm_setting_connection_get_id (s_connection));
+	}
+
+	config = build_supplicant_config (self, connection, ap);
+	if (config == NULL) {
+		nm_log_err (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): couldn't build wireless configuration.",
+		             iface);
+		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
+		goto out;
+	}
+
+	/* Hook up error signal handler to capture association errors */
+	id = g_signal_connect (priv->supplicant.iface,
+	                       "connection-error",
+	                       G_CALLBACK (supplicant_iface_connection_error_cb),
+	                       self);
+	priv->supplicant.iface_error_id = id;
+
+	if (!nm_supplicant_interface_set_config (priv->supplicant.iface, config)) {
+		nm_log_err (LOGD_DEVICE | LOGD_WIFI,
+		            "Activation (%s/wireless): couldn't send wireless "
+		            "configuration to the supplicant.", iface);
+		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
+		goto out;
+	}
+
+	if (!start_supplicant_connection_timeout (self)) {
+		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
+		goto out;
+	}
+
+	/* We'll get stage3 started when the supplicant connects */
+	ret = NM_ACT_STAGE_RETURN_POSTPONE;
+
+out:
+	if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+		cleanup_association_attempt (self, TRUE);
+
+	if (config) {
+		/* Supplicant interface object refs the config; we no longer care about
+		 * it after this function.
+		 */
+		g_object_unref (config);
+	}
+	return ret;
+}
+
+static NMActStageReturn
+real_act_stage4_get_ip4_config (NMDevice *dev,
+                                NMIP4Config **config,
+                                NMDeviceStateReason *reason)
+{
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	NMDeviceClass *parent_class;
+
+	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	/* Chain up to parent */
+	parent_class = NM_DEVICE_CLASS (nm_device_wifi_parent_class);
+	ret = parent_class->act_stage4_get_ip4_config (dev, config, reason);
+
+	if ((ret == NM_ACT_STAGE_RETURN_SUCCESS) && *config) {
+		NMConnection *connection;
+		NMSettingWireless *s_wireless;
+		guint32 mtu;
+
+		connection = nm_act_request_get_connection (nm_device_get_act_request (dev));
+		g_assert (connection);
+		s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS));
+		g_assert (s_wireless);
+
+		/* MTU override */
+		mtu = nm_setting_wireless_get_mtu (s_wireless);
+		if (mtu)
+			nm_ip4_config_set_mtu (*config, mtu);
+	}
+
+	return ret;
+}
+
+
+static NMActStageReturn
+handle_ip_config_timeout (NMDeviceWifi *self,
+                          NMConnection *connection,
+                          gboolean may_fail,
+                          gboolean *chain_up,
+                          NMDeviceStateReason *reason)
+{
+	NMAccessPoint *ap;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	gboolean auth_enforced = FALSE, encrypted = FALSE;
+
+	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	ap = nm_device_wifi_get_activation_ap (self);
+	g_assert (ap);
+
+	/* If nothing checks the security authentication information (as in
+	 * Open System WEP for example), and DHCP times out, then
+	 * the encryption key is likely wrong.  Ask the user for a new one.
+	 * Otherwise the failure likely happened after a successful authentication.
+	 */
+	auth_enforced = ap_auth_enforced (connection, ap, &encrypted);
+	if (encrypted && !auth_enforced && !may_fail) {
+		NMSettingConnection *s_con;
+
+		s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+
+		/* Activation failed, we must have bad encryption key */
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): could not get IP configuration for "
+		             "connection '%s'.",
+				     nm_device_get_iface (NM_DEVICE (self)),
+				     nm_setting_connection_get_id (s_con));
+
+		ret = handle_auth_or_fail (self, NULL, TRUE);
+		if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
+			nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+			             "Activation (%s/wireless): asking for new secrets",
+			             nm_device_get_iface (NM_DEVICE (self)));
+		} else {
+			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+		}
+	} else {
+		/* Non-encrypted network or authentication is enforced by some
+		 * entity (AP, RADIUS server, etc), but IP configure failed. Let the
+		 * superclass handle it.
+		 */
+		*chain_up = TRUE;
+	}
+
+	return ret;
+}
+
+
+static NMActStageReturn
+real_act_stage4_ip4_config_timeout (NMDevice *dev,
+                                    NMIP4Config **config,
+                                    NMDeviceStateReason *reason)
+{
+	NMActRequest *req;
+	NMConnection *connection;
+	NMSettingIP4Config *s_ip4;
+	gboolean may_fail = FALSE, chain_up = FALSE;
+	NMActStageReturn ret;
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	if (s_ip4)
+		may_fail = nm_setting_ip4_config_get_may_fail (s_ip4);
+
+	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason);
+	if (chain_up)
+		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (dev, config, reason);
+
+	return ret;
+}
+
+static NMActStageReturn
+real_act_stage4_ip6_config_timeout (NMDevice *dev,
+                                    NMIP6Config **config,
+                                    NMDeviceStateReason *reason)
+{
+	NMActRequest *req;
+	NMConnection *connection;
+	NMSettingIP6Config *s_ip6;
+	gboolean may_fail = FALSE, chain_up = FALSE;
+	NMActStageReturn ret;
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	if (s_ip6)
+		may_fail = nm_setting_ip6_config_get_may_fail (s_ip6);
+
+	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason);
+	if (chain_up)
+		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (dev, config, reason);
+
+	return ret;
+}
+
+static void
+activation_success_handler (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMAccessPoint *ap;
+	struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} };
+	NMAccessPoint *tmp_ap;
+	NMActRequest *req;
+	NMConnection *connection;
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	/* Clear wireless secrets tries on success */
+	g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
+
+	ap = nm_device_wifi_get_activation_ap (self);
+
+	/* If the AP isn't fake, it was found in the scan list and all its
+	 * details are known.
+	 */
+	if (!nm_ap_get_fake (ap))
+		goto done;
+
+	/* If the activate AP was fake, it probably won't have a BSSID at all.
+	 * But if activation was successful, the card will know the BSSID.  Grab
+	 * the BSSID off the card and fill in the BSSID of the activation AP.
+	 */
+	nm_device_wifi_get_bssid (self, &bssid);
+	if (!nm_ethernet_address_is_valid (nm_ap_get_address (ap)))
+		nm_ap_set_address (ap, &bssid);
+	if (!nm_ap_get_freq (ap))
+		nm_ap_set_freq (ap, nm_device_wifi_get_frequency (self));
+	if (!nm_ap_get_max_bitrate (ap) && nm_ap_get_user_created (ap))
+		nm_ap_set_max_bitrate (ap, nm_device_wifi_get_bitrate (self));
+
+	tmp_ap = get_active_ap (self, ap, TRUE);
+	if (tmp_ap) {
+		const GByteArray *ssid = nm_ap_get_ssid (tmp_ap);
+
+		/* Found a better match in the scan list than the fake AP.  Use it
+		 * instead.
+		 */
+
+		/* If the better match was a hidden AP, update it's SSID */
+		if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len))
+			nm_ap_set_ssid (tmp_ap, nm_ap_get_ssid (ap));
+
+		nm_act_request_set_specific_object (req, nm_ap_get_dbus_path (tmp_ap));
+
+		priv->ap_list = g_slist_remove (priv->ap_list, ap);
+		g_object_unref (ap);
+	}
+
+done:
+	periodic_update (self);
+
+	/* Reset scan interval to something reasonable */
+	priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2);
+}
+
+
+static void
+activation_failure_handler (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMAccessPoint *ap;
+	const GByteArray * ssid;
+	NMActRequest *req;
+	NMConnection *connection;
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	/* Clear wireless secrets tries on failure */
+	g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
+
+	if ((ap = nm_device_wifi_get_activation_ap (self))) {
+		if (nm_ap_get_fake (ap)) {
+			/* Fake APs are ones that don't show up in scans,
+			 * but which the user explicitly attempted to connect to.
+			 * However, if we fail on one of these, remove it from the
+			 * list because we don't have any scan or capability info
+			 * for it, and they are pretty much useless.
+			 */
+			access_point_removed (self, ap);
+			priv->ap_list = g_slist_remove (priv->ap_list, ap);
+			g_object_unref (ap);
+		}
+	}
+
+	ssid = nm_ap_get_ssid (ap);
+	nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+	             "Activation (%s) failed for access point (%s)",
+	             nm_device_get_iface (dev),
+	             ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)");
+}
+
+static gboolean
+real_can_interrupt_activation (NMDevice *dev)
+{
+	if (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static guint32
+real_get_type_capabilities (NMDevice *dev)
+{
+	return NM_DEVICE_WIFI_GET_PRIVATE (dev)->capabilities;
+}
+
+
+static gboolean
+spec_match_list (NMDevice *device, const GSList *specs)
+{
+	struct ether_addr ether;
+	char *hwaddr;
+	gboolean matched;
+
+	nm_device_wifi_get_address (NM_DEVICE_WIFI (device), &ether);
+	hwaddr = nm_ether_ntop (&ether);
+	matched = nm_match_spec_hwaddr (specs, hwaddr);
+	g_free (hwaddr);
+
+	return matched;
+}
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	gboolean clear_aps = FALSE;
+
+	if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) {
+		/* Clean up the supplicant interface because in these states the
+		 * device cannot be used.
+		 */
+		if (priv->supplicant.iface)
+			supplicant_interface_release (self);
+	}
+
+	/* Start or stop the rfkill poll worker for ipw cards */
+	if (priv->ipw_rfkill_path) {
+		if (new_state > NM_DEVICE_STATE_UNMANAGED) {
+			if (!priv->ipw_rfkill_id)
+				priv->ipw_rfkill_id = g_timeout_add_seconds (3, ipw_rfkill_state_work, self);
+		} else if (new_state <= NM_DEVICE_STATE_UNMANAGED) {
+			if (priv->ipw_rfkill_id) {
+				g_source_remove (priv->ipw_rfkill_id);
+				priv->ipw_rfkill_id = 0;
+			}
+		}
+	}
+
+	switch (new_state) {
+	case NM_DEVICE_STATE_UNMANAGED:
+		clear_aps = TRUE;
+		break;
+	case NM_DEVICE_STATE_UNAVAILABLE:
+		/* If the device is enabled and the supplicant manager is ready,
+		 * acquire a supplicant interface and transition to DISCONNECTED because
+		 * the device is now ready to use.
+		 */
+		if (priv->enabled) {
+			gboolean success;
+			struct iw_range range;
+
+			/* Wait for some drivers like ipw3945 to come back to life */
+			success = wireless_get_range (self, &range, NULL);
+
+			if (!priv->supplicant.iface)
+				supplicant_interface_acquire (self);
+		}
+		clear_aps = TRUE;
+		break;
+	case NM_DEVICE_STATE_ACTIVATED:
+		activation_success_handler (device);
+		break;
+	case NM_DEVICE_STATE_FAILED:
+		activation_failure_handler (device);
+		break;
+	case NM_DEVICE_STATE_DISCONNECTED:
+		// FIXME: ensure that the activation request is destroyed
+		break;
+	default:
+		break;
+	}
+
+	if (clear_aps)
+		remove_all_aps (self);
+}
+
+NMAccessPoint *
+nm_device_wifi_get_activation_ap (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv;
+	NMActRequest *req;
+	const char *ap_path;
+	GSList * elt;
+
+	g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NULL);
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	if (!req)
+		return NULL;
+
+	ap_path = nm_act_request_get_specific_object (req);
+	if (!ap_path)
+		return NULL;
+
+	/* Find the AP by it's object path */
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) {
+		NMAccessPoint *ap = NM_AP (elt->data);
+
+		if (!strcmp (ap_path, nm_ap_get_dbus_path (ap)))
+			return ap;
+	}
+	return NULL;
+}
+
+static void
+real_set_enabled (NMDeviceInterface *device, gboolean enabled)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMDeviceState state;
+
+	if (priv->enabled == enabled)
+		return;
+
+	priv->enabled = enabled;
+
+	nm_log_dbg (LOGD_WIFI, "(%s): device now %s",
+	            nm_device_get_iface (NM_DEVICE (device)),
+	            enabled ? "enabled" : "disabled");
+
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
+	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
+		nm_log_dbg (LOGD_WIFI, "(%s): %s blocked by UNMANAGED state",
+		            enabled ? "enable" : "disable",
+		            nm_device_get_iface (NM_DEVICE (device)));
+		return;
+	}
+
+	if (enabled) {
+		gboolean no_firmware = FALSE, success;
+		struct iw_range range;
+
+		if (state != NM_DEVICE_STATE_UNAVAILABLE)
+			nm_log_warn (LOGD_CORE, "not in expected unavailable state!");
+
+		if (!nm_device_hw_bring_up (NM_DEVICE (self), TRUE, &no_firmware)) {
+			nm_log_dbg (LOGD_WIFI, "(%s): enable blocked by failure to bring device up",
+			            nm_device_get_iface (NM_DEVICE (device)));
+
+			/* The device sucks, or HAL was lying to us about the killswitch state */
+			priv->enabled = FALSE;
+			return;
+		}
+
+		/* Wait for some drivers like ipw3945 to come back to life */
+		success = wireless_get_range (self, &range, NULL);
+
+		/* iface should be NULL here, but handle it anyway if it's not */
+		g_warn_if_fail (priv->supplicant.iface == NULL);
+		if (priv->supplicant.iface)
+			supplicant_interface_release (self);
+
+		supplicant_interface_acquire (self);
+		nm_log_dbg (LOGD_WIFI, "(%s): enable waiting on supplicant state",
+		            nm_device_get_iface (NM_DEVICE (device)));
+	} else {
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         NM_DEVICE_STATE_REASON_NONE);
+		nm_device_hw_take_down (NM_DEVICE (self), TRUE);
+	}
+}
+
+/********************************************************************/
+
+NMDevice *
+nm_device_wifi_new (const char *udi,
+                    const char *iface,
+                    const char *driver)
+{
+	g_return_val_if_fail (udi != NULL, NULL);
+	g_return_val_if_fail (iface != NULL, NULL);
+	g_return_val_if_fail (driver != NULL, NULL);
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_WIFI,
+	                                  NM_DEVICE_INTERFACE_UDI, udi,
+	                                  NM_DEVICE_INTERFACE_IFACE, iface,
+	                                  NM_DEVICE_INTERFACE_DRIVER, driver,
+	                                  NM_DEVICE_INTERFACE_TYPE_DESC, "802.11 WiFi",
+	                                  NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI,
+	                                  NM_DEVICE_INTERFACE_RFKILL_TYPE, RFKILL_TYPE_WLAN,
+	                                  NULL);
+}
+
+static void
+device_interface_init (NMDeviceInterface *iface_class)
+{
+    iface_class->set_enabled = real_set_enabled;
+}
+
+static void
+nm_device_wifi_init (NMDeviceWifi * self)
+{
+	g_signal_connect (self, "state-changed", G_CALLBACK (device_state_changed), NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (priv->disposed) {
+		G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object);
+		return;
+	}
+
+	priv->disposed = TRUE;
+
+	if (priv->periodic_source_id) {
+		g_source_remove (priv->periodic_source_id);
+		priv->periodic_source_id = 0;
+	}
+
+	/* Clean up all pending supplicant tasks */
+	while (priv->supplicant.iface_tasks)
+		finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE);
+	while (priv->supplicant.mgr_tasks)
+		finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.mgr_tasks->data, TRUE);
+
+	cleanup_association_attempt (self, TRUE);
+	supplicant_interface_release (self);
+
+	if (priv->supplicant.mgr_state_id) {
+		g_signal_handler_disconnect (priv->supplicant.mgr, priv->supplicant.mgr_state_id);
+		priv->supplicant.mgr_state_id = 0;
+	}
+
+	if (priv->supplicant.mgr) {
+		g_object_unref (priv->supplicant.mgr);
+		priv->supplicant.mgr = NULL;
+	}
+
+	if (priv->ssid) {
+		g_byte_array_free (priv->ssid, TRUE);
+		priv->ssid = NULL;
+	}
+
+	set_current_ap (self, NULL);
+	remove_all_aps (self);
+
+	g_free (priv->ipw_rfkill_path);
+	if (priv->ipw_rfkill_id) {
+		g_source_remove (priv->ipw_rfkill_id);
+		priv->ipw_rfkill_id = 0;
+	}
+
+	G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceWifi *device = NM_DEVICE_WIFI (object);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
+	struct ether_addr hw_addr;
+
+	switch (prop_id) {
+	case PROP_HW_ADDRESS:
+		nm_device_wifi_get_address (device, &hw_addr);
+		g_value_take_string (value, nm_ether_ntop (&hw_addr));
+		break;
+	case PROP_MODE:
+		g_value_set_uint (value, nm_device_wifi_get_mode (device));
+		break;
+	case PROP_BITRATE:
+		g_value_set_uint (value, priv->rate);
+		break;
+	case PROP_CAPABILITIES:
+		g_value_set_uint (value, priv->capabilities);
+		break;
+	case PROP_ACTIVE_ACCESS_POINT:
+		if (priv->current_ap)
+			g_value_set_boxed (value, nm_ap_get_dbus_path (priv->current_ap));
+		else
+			g_value_set_boxed (value, "/");
+		break;
+	case PROP_SCANNING:
+		g_value_set_boolean (value, nm_supplicant_interface_get_scanning (priv->supplicant.iface));
+		break;
+	case PROP_IPW_RFKILL_STATE:
+		g_value_set_uint (value, nm_device_wifi_get_ipw_rfkill_state (device));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_IPW_RFKILL_STATE:
+		/* construct only */
+		priv->ipw_rfkill_state = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+
+static void
+nm_device_wifi_class_init (NMDeviceWifiClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMDeviceWifiPrivate));
+
+	object_class->constructor = constructor;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+
+	parent_class->get_type_capabilities = real_get_type_capabilities;
+	parent_class->get_generic_capabilities = real_get_generic_capabilities;
+	parent_class->hw_is_up = real_hw_is_up;
+	parent_class->hw_bring_up = real_hw_bring_up;
+	parent_class->hw_take_down = real_hw_take_down;
+	parent_class->is_up = real_is_up;
+	parent_class->bring_up = real_bring_up;
+	parent_class->take_down = real_take_down;
+	parent_class->update_hw_address = real_update_hw_address;
+	parent_class->get_best_auto_connection = real_get_best_auto_connection;
+	parent_class->is_available = real_is_available;
+	parent_class->connection_secrets_updated = real_connection_secrets_updated;
+	parent_class->check_connection_compatible = real_check_connection_compatible;
+
+	parent_class->act_stage1_prepare = real_act_stage1_prepare;
+	parent_class->act_stage2_config = real_act_stage2_config;
+	parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
+	parent_class->act_stage4_ip4_config_timeout = real_act_stage4_ip4_config_timeout;
+	parent_class->act_stage4_ip6_config_timeout = real_act_stage4_ip6_config_timeout;
+	parent_class->deactivate = real_deactivate;
+	parent_class->deactivate_quickly = real_deactivate_quickly;
+	parent_class->can_interrupt_activation = real_can_interrupt_activation;
+	parent_class->spec_match_list = spec_match_list;
+
+	klass->scanning_allowed = scanning_allowed;
+
+	/* Properties */
+	g_object_class_install_property (object_class, PROP_HW_ADDRESS,
+		g_param_spec_string (NM_DEVICE_WIFI_HW_ADDRESS,
+		                     "MAC Address",
+		                     "Hardware MAC address",
+		                     NULL,
+		                     G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_MODE,
+		g_param_spec_uint (NM_DEVICE_WIFI_MODE,
+		                   "Mode",
+		                   "Mode",
+		                   NM_802_11_MODE_UNKNOWN,
+		                   NM_802_11_MODE_INFRA,
+		                   NM_802_11_MODE_INFRA,
+		                   G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_BITRATE,
+		g_param_spec_uint (NM_DEVICE_WIFI_BITRATE,
+		                   "Bitrate",
+		                   "Bitrate",
+		                   0, G_MAXUINT32, 0,
+		                   G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_ACTIVE_ACCESS_POINT,
+		g_param_spec_boxed (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT,
+		                    "Active access point",
+		                    "Currently active access point",
+		                    DBUS_TYPE_G_OBJECT_PATH,
+		                    G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_CAPABILITIES,
+		g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES,
+		                   "Wireless Capabilities",
+		                   "Wireless Capabilities",
+		                   0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE,
+		                   G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_SCANNING,
+		g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING,
+		                   "Scanning",
+		                   "Scanning",
+		                   FALSE,
+		                   G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	g_object_class_install_property (object_class, PROP_IPW_RFKILL_STATE,
+		g_param_spec_uint (NM_DEVICE_WIFI_IPW_RFKILL_STATE,
+		                   "IpwRfkillState",
+		                   "ipw rf-kill state",
+		                   RFKILL_UNBLOCKED, RFKILL_HARD_BLOCKED, RFKILL_UNBLOCKED,
+		                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	/* Signals */
+	signals[ACCESS_POINT_ADDED] =
+		g_signal_new ("access-point-added",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_added),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__OBJECT,
+		              G_TYPE_NONE, 1,
+		              G_TYPE_OBJECT);
+
+	signals[ACCESS_POINT_REMOVED] =
+		g_signal_new ("access-point-removed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_removed),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__OBJECT,
+		              G_TYPE_NONE, 1,
+		              G_TYPE_OBJECT);
+
+	signals[HIDDEN_AP_FOUND] =
+		g_signal_new ("hidden-ap-found",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMDeviceWifiClass, hidden_ap_found),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__OBJECT,
+		              G_TYPE_NONE, 1,
+		              G_TYPE_OBJECT);
+
+	signals[PROPERTIES_CHANGED] =
+		nm_properties_changed_signal_new (object_class,
+		                                  G_STRUCT_OFFSET (NMDeviceWifiClass, properties_changed));
+
+	signals[SCANNING_ALLOWED] =
+		g_signal_new ("scanning-allowed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_allowed),
+		              scanning_allowed_accumulator, NULL,
+		              _nm_marshal_BOOLEAN__VOID,
+		              G_TYPE_BOOLEAN, 0);
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), &dbus_glib_nm_device_wifi_object_info);
+
+	dbus_g_error_domain_register (NM_WIFI_ERROR, NULL, NM_TYPE_WIFI_ERROR);
+}
+
+
diff --git a/src/nm-device-wifi.h b/src/nm-device-wifi.h
new file mode 100644
index 00000000..11ac8857
--- /dev/null
+++ b/src/nm-device-wifi.h
@@ -0,0 +1,107 @@
+/* -*- 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_DEVICE_WIFI_H
+#define NM_DEVICE_WIFI_H
+
+#include <glib-object.h>
+#include <dbus/dbus.h>
+#include <net/ethernet.h>
+
+#include "nm-rfkill.h"
+#include "nm-device.h"
+#include "nm-wifi-ap.h"
+
+struct NMAccessPointList;
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_WIFI			(nm_device_wifi_get_type ())
+#define NM_DEVICE_WIFI(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifi))
+#define NM_DEVICE_WIFI_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
+#define NM_IS_DEVICE_WIFI(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI))
+#define NM_IS_DEVICE_WIFI_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_WIFI))
+#define NM_DEVICE_WIFI_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
+
+
+#define NM_DEVICE_WIFI_HW_ADDRESS          "hw-address"
+#define NM_DEVICE_WIFI_MODE                "mode"
+#define NM_DEVICE_WIFI_BITRATE             "bitrate"
+#define NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT "active-access-point"
+#define NM_DEVICE_WIFI_CAPABILITIES        "wireless-capabilities"
+#define NM_DEVICE_WIFI_SCANNING            "scanning"
+#define NM_DEVICE_WIFI_IPW_RFKILL_STATE    "ipw-rfkill-state"
+
+#ifndef NM_DEVICE_WIFI_DEFINED
+#define NM_DEVICE_WIFI_DEFINED
+typedef struct _NMDeviceWifi NMDeviceWifi;
+#endif
+
+typedef struct _NMDeviceWifiClass NMDeviceWifiClass;
+typedef struct _NMDeviceWifiPrivate NMDeviceWifiPrivate;
+
+struct _NMDeviceWifi
+{
+	NMDevice parent;
+
+	/*< private >*/
+	NMDeviceWifiPrivate *priv;
+};
+
+struct _NMDeviceWifiClass
+{
+	NMDeviceClass parent;
+
+	/* Signals */
+	void (*access_point_added)   (NMDeviceWifi *device, NMAccessPoint *ap);
+	void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap);
+	void (*hidden_ap_found)      (NMDeviceWifi *device, NMAccessPoint *ap);
+	void (*properties_changed)   (NMDeviceWifi *device, GHashTable *properties);
+	gboolean (*scanning_allowed) (NMDeviceWifi *device);
+};
+
+
+GType nm_device_wifi_get_type (void);
+
+NMDevice *nm_device_wifi_new (const char *udi,
+                              const char *iface,
+                              const char *driver);
+
+void nm_device_wifi_get_address (NMDeviceWifi *dev,
+								   struct ether_addr *addr);
+
+void			nm_device_wifi_get_bssid (NMDeviceWifi *dev,
+                                                    struct ether_addr *bssid);
+
+const GByteArray *	nm_device_wifi_get_ssid (NMDeviceWifi *self);
+
+gboolean		nm_device_wifi_set_mode (NMDeviceWifi *self,
+										 const NM80211Mode mode);
+
+NM80211Mode	nm_device_wifi_get_mode (NMDeviceWifi *self);
+
+NMAccessPoint * nm_device_wifi_get_activation_ap (NMDeviceWifi *self);
+
+RfKillState nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self);
+
+G_END_DECLS
+
+#endif	/* NM_DEVICE_WIFI_H */
diff --git a/src/nm-device.c b/src/nm-device.c
new file mode 100644
index 00000000..176d90f9
--- /dev/null
+++ b/src/nm-device.c
@@ -0,0 +1,3827 @@
+/* -*- 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 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+#include "nm-glib-compat.h"
+#include "nm-device-interface.h"
+#include "nm-device.h"
+#include "nm-device-private.h"
+#include "NetworkManagerUtils.h"
+#include "nm-system.h"
+#include "nm-dhcp-manager.h"
+#include "nm-dbus-manager.h"
+#include "nm-named-manager.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "nm-netlink-monitor.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-connection.h"
+#include "nm-dnsmasq-manager.h"
+#include "nm-dhcp4-config.h"
+#include "nm-ip6-manager.h"
+#include "nm-marshal.h"
+#include "nm-rfkill.h"
+
+#define NM_ACT_REQUEST_IP4_CONFIG "nm-act-request-ip4-config"
+#define NM_ACT_REQUEST_IP6_CONFIG "nm-act-request-ip6-config"
+
+static void device_interface_init (NMDeviceInterface *device_interface_class);
+
+G_DEFINE_TYPE_EXTENDED (NMDevice, nm_device, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT,
+						G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE, device_interface_init))
+
+enum {
+	AUTOCONNECT_ALLOWED,
+	LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
+
+typedef struct {
+	gboolean disposed;
+	gboolean initialized;
+
+	NMDeviceState state;
+	guint         failed_to_disconnected_id;
+	guint         unavailable_to_disconnected_id;
+
+	char *        udi;
+	char *        path;
+	char *        iface;   /* may change, could be renamed by user */
+	int           ifindex;
+	char *        ip_iface;
+	int           ip_ifindex;
+	NMDeviceType  type;
+	char *        type_desc;
+	guint32       capabilities;
+	char *        driver;
+	gboolean      managed; /* whether managed by NM or not */
+	RfKillType    rfkill_type;
+
+	guint32         ip4_address;
+
+	NMActRequest *  act_request;
+	guint           act_source_id;
+	gpointer        act_source_func;
+	guint           act_source6_id;
+	gpointer        act_source6_func;
+	gulong          secrets_updated_id;
+	gulong          secrets_failed_id;
+
+	gboolean        ip4_ready;
+	gboolean        ip6_ready;
+
+	/* Generic DHCP stuff */
+	NMDHCPManager * dhcp_manager;
+	guint32         dhcp_timeout;
+	GByteArray *    dhcp_anycast_address;
+
+	/* IP4 configuration info */
+	NMIP4Config *   ip4_config;			/* Config from DHCP, PPP, or system config files */
+	NMDHCPClient *  dhcp4_client;
+	gulong          dhcp4_state_sigid;
+	gulong          dhcp4_timeout_sigid;
+	NMDHCP4Config * dhcp4_config;
+
+	/* dnsmasq stuff for shared connections */
+	NMDnsMasqManager *dnsmasq_manager;
+	gulong            dnsmasq_state_id;
+
+	/* avahi-autoipd stuff */
+	GPid    aipd_pid;
+	guint   aipd_watch;
+	guint   aipd_timeout;
+	guint32 aipd_addr;
+
+	/* IP6 configuration info */
+	NMIP6Config *  ip6_config;
+	NMIP6Manager * ip6_manager;
+	gulong         ip6_addrconf_sigid;
+	gulong         ip6_config_changed_sigid;
+	gboolean       ip6_waiting_for_config;
+
+	char *         ip6_accept_ra_path;
+	guint32        ip6_accept_ra_save;
+
+	NMDHCPClient *  dhcp6_client;
+	guint32         dhcp6_mode;
+	gulong          dhcp6_state_sigid;
+	gulong          dhcp6_timeout_sigid;
+	NMDHCP6Config * dhcp6_config;
+
+	/* inhibit autoconnect feature */
+	gboolean	autoconnect_inhibit;
+} NMDevicePrivate;
+
+static gboolean check_connection_compatible (NMDeviceInterface *device,
+                                             NMConnection *connection,
+                                             GError **error);
+static gboolean nm_device_activate (NMDeviceInterface *device,
+                                    NMActRequest *req,
+                                    GError **error);
+static void nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason);
+static gboolean device_disconnect (NMDeviceInterface *device, GError **error);
+static gboolean spec_match_list (NMDeviceInterface *device, const GSList *specs);
+static NMConnection *connection_match_config (NMDeviceInterface *device, const GSList *connections);
+
+static void nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self, int family);
+
+static void nm_device_take_down (NMDevice *dev, gboolean wait, NMDeviceStateReason reason);
+
+static gboolean nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware);
+static gboolean nm_device_is_up (NMDevice *self);
+
+static gboolean nm_device_set_ip4_config (NMDevice *dev,
+                                          NMIP4Config *config,
+                                          gboolean assumed,
+                                          NMDeviceStateReason *reason);
+static gboolean nm_device_set_ip6_config (NMDevice *dev,
+                                          NMIP6Config *config,
+                                          gboolean assumed,
+                                          NMDeviceStateReason *reason);
+
+static NMActStageReturn dhcp6_start (NMDevice *self,
+                                     NMConnection *connection,
+                                     guint32 dhcp_opt,
+                                     NMDeviceStateReason *reason);
+
+static void addrconf6_cleanup (NMDevice *self);
+static void dhcp6_cleanup (NMDevice *self, gboolean stop);
+static void dhcp4_cleanup (NMDevice *self, gboolean stop);
+
+
+static void
+device_interface_init (NMDeviceInterface *device_interface_class)
+{
+	/* interface implementation */
+	device_interface_class->check_connection_compatible = check_connection_compatible;
+	device_interface_class->activate = nm_device_activate;
+	device_interface_class->deactivate = nm_device_deactivate;
+	device_interface_class->disconnect = device_disconnect;
+	device_interface_class->spec_match_list = spec_match_list;
+	device_interface_class->connection_match_config = connection_match_config;
+}
+
+
+static void
+nm_device_init (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	priv->type = NM_DEVICE_TYPE_UNKNOWN;
+	priv->capabilities = NM_DEVICE_CAP_NONE;
+	priv->state = NM_DEVICE_STATE_UNMANAGED;
+	priv->dhcp_timeout = 0;
+	priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
+}
+
+static void
+update_accept_ra_save (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+	const char *ip_iface;
+	char *new_path;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	ip_iface = nm_device_get_ip_iface (self);
+
+	new_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra", ip_iface);
+	g_assert (new_path);
+
+	if (priv->ip6_accept_ra_path) {
+		/* If the IP iface is different from before, use the new value */
+		if (!strcmp (new_path, priv->ip6_accept_ra_path)) {
+			g_free (new_path);
+			return;
+		}
+		g_free (priv->ip6_accept_ra_path);
+	}
+
+	/* Grab the original value of "accept_ra" so we can restore it when NM exits */
+	priv->ip6_accept_ra_path = new_path;
+	if (!nm_utils_get_proc_sys_net_value (priv->ip6_accept_ra_path,
+	                                      ip_iface,
+	                                      &priv->ip6_accept_ra_save)) {
+		g_free (priv->ip6_accept_ra_path);
+		priv->ip6_accept_ra_path = NULL;
+	}
+}
+
+static GObject*
+constructor (GType type,
+			 guint n_construct_params,
+			 GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	NMDevice *dev;
+	NMDevicePrivate *priv;
+
+	object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type,
+													   n_construct_params,
+													   construct_params);
+	if (!object)
+		return NULL;
+
+	dev = NM_DEVICE (object);
+	priv = NM_DEVICE_GET_PRIVATE (dev);
+
+	if (!priv->udi) {
+		nm_log_err (LOGD_DEVICE, "No device udi provided, ignoring");
+		goto error;
+	}
+
+	if (!priv->iface) {
+		nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring");
+		goto error;
+	}
+
+	priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev);
+	if (!(priv->capabilities & NM_DEVICE_CAP_NM_SUPPORTED)) {
+		nm_log_warn (LOGD_DEVICE, "(%s): Device unsupported, ignoring.", priv->iface);
+		goto error;
+	}
+
+	if (NM_DEVICE_GET_CLASS (dev)->update_hw_address)
+		NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev);
+
+	priv->dhcp_manager = nm_dhcp_manager_get ();
+
+	update_accept_ra_save (dev);
+
+	priv->initialized = TRUE;
+	return object;
+
+error:
+	g_object_unref (dev);
+	return NULL;
+}
+
+static gboolean
+nm_device_hw_is_up (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	if (NM_DEVICE_GET_CLASS (self)->hw_is_up)
+		return NM_DEVICE_GET_CLASS (self)->hw_is_up (self);
+
+	return TRUE;
+}
+
+static guint32
+real_get_generic_capabilities (NMDevice *dev)
+{
+	return 0;
+}
+
+void
+nm_device_set_path (NMDevice *self, const char *path)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (self != NULL);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->path == NULL);
+
+	priv->path = g_strdup (path);
+}
+
+const char *
+nm_device_get_path (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->path;
+}
+
+const char *
+nm_device_get_udi (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->udi;
+}
+
+/*
+ * Get/set functions for iface
+ */
+const char *
+nm_device_get_iface (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->iface;
+}
+
+int
+nm_device_get_ifindex (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, 0);
+
+	return NM_DEVICE_GET_PRIVATE (self)->ifindex;
+}
+
+const char *
+nm_device_get_ip_iface (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (self != NULL, NULL);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	/* If it's not set, default to iface */
+	return priv->ip_iface ? priv->ip_iface : priv->iface;
+}
+
+int
+nm_device_get_ip_ifindex (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (self != NULL, 0);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	/* If it's not set, default to iface */
+	return priv->ip_iface ? priv->ip_ifindex : priv->ifindex;
+}
+
+void
+nm_device_set_ip_iface (NMDevice *self, const char *iface)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_free (priv->ip_iface);
+	priv->ip_ifindex = 0;
+
+	priv->ip_iface = g_strdup (iface);
+	if (priv->ip_iface) {
+		priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface);
+		if (!priv->ip_ifindex) {
+			nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface);
+		}
+	}
+}
+
+
+/*
+ * Get/set functions for driver
+ */
+const char *
+nm_device_get_driver (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->driver;
+}
+
+
+/*
+ * Get/set functions for type
+ */
+NMDeviceType
+nm_device_get_device_type (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_TYPE_UNKNOWN);
+
+	return NM_DEVICE_GET_PRIVATE (self)->type;
+}
+
+
+int
+nm_device_get_priority (NMDevice *dev)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (dev), -1);
+
+	return (int) nm_device_get_device_type (dev);
+}
+
+
+/*
+ * Accessor for capabilities
+ */
+guint32
+nm_device_get_capabilities (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NM_DEVICE_CAP_NONE);
+
+	return NM_DEVICE_GET_PRIVATE (self)->capabilities;
+}
+
+/*
+ * Accessor for type-specific capabilities
+ */
+guint32
+nm_device_get_type_capabilities (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NM_DEVICE_CAP_NONE);
+
+	return NM_DEVICE_GET_CLASS (self)->get_type_capabilities (self);
+}
+
+static guint32
+real_get_type_capabilities (NMDevice *self)
+{
+	return NM_DEVICE_CAP_NONE;
+}
+
+
+const char *
+nm_device_get_type_desc (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->type_desc;
+}
+
+/*
+ * nm_device_get_act_request
+ *
+ * Return the devices activation request, if any.
+ *
+ */
+NMActRequest *
+nm_device_get_act_request (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->act_request;
+}
+
+
+gboolean
+nm_device_is_available (NMDevice *self)
+{
+	if (NM_DEVICE_GET_CLASS (self)->is_available)
+		return NM_DEVICE_GET_CLASS (self)->is_available (self);
+	return TRUE;
+}
+
+static gboolean
+autoconnect_allowed_accumulator (GSignalInvocationHint *ihint,
+                                 GValue *return_accu,
+                                 const GValue *handler_return, gpointer data)
+{
+	if (!g_value_get_boolean (handler_return))
+		g_value_set_boolean (return_accu, FALSE);
+	return TRUE;
+}
+
+gboolean
+nm_device_autoconnect_allowed (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	GValue instance = { 0, };
+	GValue retval = { 0, };
+
+	g_value_init (&instance, G_TYPE_OBJECT);
+	g_value_take_object (&instance, self);
+
+	g_value_init (&retval, G_TYPE_BOOLEAN);
+	if (priv->autoconnect_inhibit)
+		g_value_set_boolean (&retval, FALSE);
+	else
+		g_value_set_boolean (&retval, TRUE);
+
+	/* Use g_signal_emitv() rather than g_signal_emit() to avoid the return
+	 * value being changed if no handlers are connected */
+	g_signal_emitv (&instance, signals[AUTOCONNECT_ALLOWED], 0, &retval);
+	return g_value_get_boolean (&retval);
+}
+
+NMConnection *
+nm_device_get_best_auto_connection (NMDevice *dev,
+                                    GSList *connections,
+                                    char **specific_object)
+{
+	guint32 caps;
+
+	g_return_val_if_fail (NM_IS_DEVICE (dev), NULL);
+	g_return_val_if_fail (specific_object != NULL, NULL);
+	g_return_val_if_fail (*specific_object == NULL, NULL);
+
+	caps = nm_device_get_capabilities (dev);
+	/* Don't use devices that SUCK */
+	if (!(caps & NM_DEVICE_CAP_NM_SUPPORTED))
+		return NULL;
+
+	if (!NM_DEVICE_GET_CLASS (dev)->get_best_auto_connection)
+		return NULL;
+
+	return NM_DEVICE_GET_CLASS (dev)->get_best_auto_connection (dev, connections, specific_object);
+}
+
+static void
+dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+
+	switch (status) {
+	case NM_DNSMASQ_STATUS_DEAD:
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+activation_source_clear (NMDevice *self, gboolean remove_source, int family)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	guint *act_source_id;
+	gpointer *act_source_func;
+
+	if (family == AF_INET6) {
+		act_source_id = &priv->act_source6_id;
+		act_source_func = &priv->act_source6_func;
+	} else {
+		act_source_id = &priv->act_source_id;
+		act_source_func = &priv->act_source_func;
+	}
+
+	if (*act_source_id) {
+		if (remove_source)
+			g_source_remove (*act_source_id);
+		*act_source_id = 0;
+		*act_source_func = NULL;
+	}
+}
+
+static void
+activation_source_schedule (NMDevice *self, GSourceFunc func, int family)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	guint *act_source_id;
+	gpointer *act_source_func;
+
+	if (family == AF_INET6) {
+		act_source_id = &priv->act_source6_id;
+		act_source_func = &priv->act_source6_func;
+	} else {
+		act_source_id = &priv->act_source_id;
+		act_source_func = &priv->act_source_func;
+	}
+
+	if (*act_source_id) {
+		nm_log_err (LOGD_DEVICE, "activation stage already scheduled");
+	}
+
+	/* Don't bother rescheduling the same function that's about to
+	 * run anyway.  Fixes issues with crappy wireless drivers sending
+	 * streams of associate events before NM has had a chance to process
+	 * the first one.
+	 */
+	if (!*act_source_id || (*act_source_func != func)) {
+		activation_source_clear (self, TRUE, family);
+		*act_source_id = g_idle_add (func, self);
+		*act_source_func = func;
+	}
+}
+
+gboolean
+nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6)
+{
+	NMActRequest *req;
+	NMConnection *connection;
+	NMSettingIP4Config *s_ip4;
+	NMSettingIP6Config *s_ip6;
+
+	g_return_val_if_fail (self != NULL, TRUE);
+
+	req = nm_device_get_act_request (self);
+	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	/* Fail the connection if the failed IP method is required to complete */
+	if (ip6) {
+		s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+		if (s_ip6 && !nm_setting_ip6_config_get_may_fail (s_ip6))
+			return TRUE;
+	} else {
+		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+		if (s_ip4 && !nm_setting_ip4_config_get_may_fail (s_ip4))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+ip6_addrconf_complete (NMIP6Manager *ip6_manager,
+                       int ifindex,
+                       guint dhcp_opts,
+                       gboolean success,
+                       gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActRequest *req;
+	NMConnection *connection;
+	NMActStageReturn ret;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMDeviceState state;
+
+	if (ifindex != nm_device_get_ip_ifindex (self))
+		return;
+	req = nm_device_get_act_request (self);
+	if (!req)
+		return;
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	if (!priv->ip6_waiting_for_config)
+		return;
+
+	priv->ip6_waiting_for_config = FALSE;
+
+	if (!success) {
+		nm_device_activate_schedule_stage4_ip6_config_timeout (self);
+		return;
+	}
+
+	priv->dhcp6_mode = dhcp_opts;
+
+	/* If addrconf is all that's required, we're done */
+	if (priv->dhcp6_mode == IP6_DHCP_OPT_NONE) {
+		nm_device_activate_schedule_stage4_ip6_config_get (self);
+		return;
+	}
+
+	/* If the router said to use DHCP for managed or otherconf, do it */
+
+	/* Don't re-start DHCPv6 if it's already in progress */
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
+	if ((state != NM_DEVICE_STATE_IP_CONFIG) || priv->dhcp6_client)
+		return;
+
+	nm_log_info (LOGD_DEVICE | LOGD_DHCP6,
+	             "Activation (%s) Stage 3 of 5 (IP Configure Start) starting DHCPv6"
+	             " as requested by IPv6 router...",
+	             priv->iface);
+
+	ret = dhcp6_start (self, connection, priv->dhcp6_mode, &reason);
+	switch (ret) {
+	case NM_ACT_STAGE_RETURN_SUCCESS:
+		/* Shouldn't get this, but handle it anyway */
+		g_warn_if_reached ();
+		nm_device_activate_schedule_stage4_ip6_config_get (self);
+		break;
+	case NM_ACT_STAGE_RETURN_POSTPONE:
+		/* Success; wait for DHCPv6 to complete */
+		break;
+	default:
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		break;
+	}
+}
+
+static void
+ip6_config_changed (NMIP6Manager *ip6_manager,
+                    int ifindex,
+                    guint dhcp_opts,
+                    gboolean success,
+                    gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+
+	if (ifindex != nm_device_get_ip_ifindex (self))
+		return;
+	if (!nm_device_get_act_request (self))
+		return;
+
+	/* FIXME: re-run DHCPv6 here to get any new nameservers or whatever */
+
+	if (!success && (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) {
+		nm_device_state_changed (self,
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		return;
+	}
+
+	nm_device_activate_schedule_stage4_ip6_config_get (self);
+}
+
+static gboolean
+ip6_method_matches (NMConnection *connection, const char *match)
+{
+	NMSettingIP6Config *s_ip6;
+	const char *method = NULL;
+
+	s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	if (s_ip6) {
+		method = nm_setting_ip6_config_get_method (s_ip6);
+		g_assert (method);
+	}
+
+	/* Treat missing IP6 setting as IGNORE */
+	if (!s_ip6 && !strcmp (match, NM_SETTING_IP6_CONFIG_METHOD_IGNORE))
+		return TRUE;
+
+	return method && !strcmp (method, match);
+}
+
+static gboolean
+addrconf6_setup (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActRequest *req;
+	NMConnection *connection;
+	NMSettingIP6Config *s_ip6;
+
+	req = nm_device_get_act_request (self);
+	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	if (!priv->ip6_manager) {
+		priv->ip6_manager = nm_ip6_manager_get ();
+		priv->ip6_addrconf_sigid = g_signal_connect (priv->ip6_manager,
+		                                             "addrconf-complete",
+		                                             G_CALLBACK (ip6_addrconf_complete),
+		                                             self);
+		priv->ip6_config_changed_sigid = g_signal_connect (priv->ip6_manager,
+		                                                   "config-changed",
+		                                                   G_CALLBACK (ip6_config_changed),
+		                                                   self);
+	}
+
+	s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	nm_ip6_manager_prepare_interface (priv->ip6_manager,
+	                                  nm_device_get_ip_ifindex (self),
+	                                  s_ip6,
+	                                  priv->ip6_accept_ra_path);
+	priv->ip6_waiting_for_config = TRUE;
+
+	return TRUE;
+}
+
+static void
+addrconf6_cleanup (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!priv->ip6_manager)
+		return;
+
+	if (priv->ip6_addrconf_sigid) {
+		g_signal_handler_disconnect (priv->ip6_manager,
+		                             priv->ip6_addrconf_sigid);
+		priv->ip6_addrconf_sigid = 0;
+	}
+	if (priv->ip6_config_changed_sigid) {
+		g_signal_handler_disconnect (priv->ip6_manager,
+		                             priv->ip6_config_changed_sigid);
+		priv->ip6_config_changed_sigid = 0;
+	}
+
+	nm_ip6_manager_cancel_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self));
+	g_object_unref (priv->ip6_manager);
+	priv->ip6_manager = NULL;
+}
+
+static NMActStageReturn
+real_act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
+{
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+/*
+ * nm_device_activate_stage1_device_prepare
+ *
+ * Prepare for device activation
+ *
+ */
+static gboolean
+nm_device_activate_stage1_device_prepare (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const char *iface;
+	NMActStageReturn ret;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	/* Clear the activation source ID now that this stage has run */
+	activation_source_clear (self, FALSE, 0);
+
+	priv->ip4_ready = priv->ip6_ready = FALSE;
+
+	iface = nm_device_get_iface (self);
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface);
+	nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
+
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
+		goto out;
+	} else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	}
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+
+	nm_device_activate_schedule_stage2_device_config (self);
+
+out:
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) complete.", iface);
+	return FALSE;
+}
+
+
+/*
+ * nm_device_activate_schedule_stage1_device_prepare
+ *
+ * Prepare a device for activation
+ *
+ */
+void
+nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->act_request);
+
+	activation_source_schedule (self, nm_device_activate_stage1_device_prepare, 0);
+
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) scheduled...",
+	             nm_device_get_iface (self));
+}
+
+static NMActStageReturn
+real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	/* Nothing to do */
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+/*
+ * nm_device_activate_stage2_device_config
+ *
+ * Determine device parameters and set those on the device, ie
+ * for wireless devices, set SSID, keys, etc.
+ *
+ */
+static gboolean
+nm_device_activate_stage2_device_config (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	const char *     iface;
+	NMActStageReturn ret;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	gboolean no_firmware = FALSE;
+
+	/* Clear the activation source ID now that this stage has run */
+	activation_source_clear (self, FALSE, 0);
+
+	iface = nm_device_get_iface (self);
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) starting...", iface);
+	nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
+
+	if (!nm_device_bring_up (self, FALSE, &no_firmware)) {
+		if (no_firmware)
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING);
+		else
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+		goto out;
+	}
+
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
+		goto out;
+	else if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+	{
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	}
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);	
+
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) successful.", iface);
+
+	nm_device_activate_schedule_stage3_ip_config_start (self);
+
+out:
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) complete.", iface);
+	return FALSE;
+}
+
+
+/*
+ * nm_device_activate_schedule_stage2_device_config
+ *
+ * Schedule setup of the hardware device
+ *
+ */
+void
+nm_device_activate_schedule_stage2_device_config (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->act_request);
+
+	activation_source_schedule (self, nm_device_activate_stage2_device_config, 0);
+
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) scheduled...",
+	         nm_device_get_iface (self));
+}
+
+static void
+aipd_timeout_remove (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->aipd_timeout) {
+		g_source_remove (priv->aipd_timeout);
+		priv->aipd_timeout = 0;
+	}
+}
+
+static void
+aipd_cleanup (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->aipd_watch) {
+		g_source_remove (priv->aipd_watch);
+		priv->aipd_watch = 0;
+	}
+
+	if (priv->aipd_pid > 0) {
+		kill (priv->aipd_pid, SIGKILL);
+
+		/* ensure the child is reaped */
+		nm_log_dbg (LOGD_AUTOIP4, "waiting for avahi-autoipd pid %d to exit", priv->aipd_pid);
+		waitpid (priv->aipd_pid, NULL, 0);
+		nm_log_dbg (LOGD_AUTOIP4, "avahi-autoip pid %d cleaned up", priv->aipd_pid);
+
+		priv->aipd_pid = -1;
+	}
+
+	aipd_timeout_remove (self);
+
+	priv->aipd_addr = 0;
+}
+
+static NMIP4Config *
+aipd_get_ip4_config (NMDevice *self, NMDeviceStateReason *reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMIP4Config *config = NULL;
+	NMIP4Address *addr;
+	NMIP4Route *route;
+
+	g_return_val_if_fail (priv->aipd_addr > 0, NULL);
+
+	config = nm_ip4_config_new ();
+	if (!config) {
+		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		return NULL;
+	}
+
+	addr = nm_ip4_address_new ();
+	nm_ip4_address_set_address (addr, (guint32) priv->aipd_addr);
+	nm_ip4_address_set_prefix (addr, 16);
+	nm_ip4_config_take_address (config, addr);
+
+	/* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */
+	route = nm_ip4_route_new ();
+	nm_ip4_route_set_dest (route, (guint32) htonl (0xE0000000L));
+	nm_ip4_route_set_prefix (route, 4);
+	nm_ip4_route_set_next_hop (route, (guint32) 0);
+	nm_ip4_route_set_metric (route, 0);
+	nm_ip4_config_take_route (config, route);
+
+	return config;	
+}
+
+static gboolean
+handle_autoip_change (NMDevice *self, NMDeviceStateReason *reason)
+{
+	NMActRequest *req;
+	NMConnection *connection;
+	NMIP4Config *config;
+
+	g_return_val_if_fail (reason != NULL, FALSE);
+
+	config = aipd_get_ip4_config (self, reason);
+	if (!config) {
+		nm_log_err (LOGD_AUTOIP4, "failed to get autoip config for rebind");
+		return FALSE;
+	}
+
+	req = nm_device_get_act_request (self);
+	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config);
+
+	if (!nm_device_set_ip4_config (self, config, FALSE, reason)) {
+		nm_log_err (LOGD_AUTOIP4, "(%s): failed to update IP4 config in response to autoip event.",
+		            nm_device_get_iface (self));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+#define IPV4LL_NETWORK (htonl (0xA9FE0000L))
+#define IPV4LL_NETMASK (htonl (0xFFFF0000L))
+
+void
+nm_device_handle_autoip4_event (NMDevice *self,
+                                const char *event,
+                                const char *address)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActRequest *req;
+	NMConnection *connection = NULL;
+	NMSettingIP4Config *s_ip4 = NULL;
+	NMDeviceState state;
+	const char *iface, *method = NULL;
+
+	g_return_if_fail (event != NULL);
+
+	req = nm_device_get_act_request (self);
+	if (!req)
+		return;
+
+	connection = nm_act_request_get_connection (req);
+	if (!connection)
+		return;
+
+	/* Ignore if the connection isn't an AutoIP connection */
+	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	if (s_ip4)
+		method = nm_setting_ip4_config_get_method (s_ip4);
+
+	if (!s_ip4 || !method || strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
+		return;
+
+	iface = nm_device_get_iface (self);
+	state = nm_device_get_state (self);
+
+	if (strcmp (event, "BIND") == 0) {
+		struct in_addr ip;
+		NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+		if (inet_pton (AF_INET, address, &ip) <= 0) {
+			nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd.",
+			            iface, address);
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR);
+			return;
+		}
+
+		if ((ip.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) {
+			nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd (not link-local).",
+			            iface, address);
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR);
+			return;
+		}
+
+		switch (state) {
+		case NM_DEVICE_STATE_IP_CONFIG:
+			if (priv->aipd_addr) {
+				nm_log_warn (LOGD_AUTOIP4, "(%s): already have autoip address!", iface);
+				return;
+			}
+
+			priv->aipd_addr = ip.s_addr;
+			aipd_timeout_remove (self);
+			nm_device_activate_schedule_stage4_ip4_config_get (self);
+			break;
+		case NM_DEVICE_STATE_ACTIVATED:
+			priv->aipd_addr = ip.s_addr;
+			if (!handle_autoip_change (self, &reason))
+				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+			break;
+		default:
+			nm_log_warn (LOGD_AUTOIP4, "(%s): unexpected avahi-autoip event %s for %s.",
+			            iface, event, address);
+			break;
+		}
+	} else {
+		nm_log_warn (LOGD_AUTOIP4, "(%s): autoip address %s no longer valid because '%s'.",
+		            iface, address, event);
+
+		/* The address is gone; terminate the connection or fail activation */
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+	}
+}
+
+static void
+aipd_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceState state;
+	const char *iface;
+
+	if (!priv->aipd_watch)
+		return;
+	priv->aipd_watch = 0;
+
+	iface = nm_device_get_iface (self);
+
+	if (WIFEXITED (status)) {
+		nm_log_dbg (LOGD_AUTOIP4, "(%s): avahi-autoipd exited with error code %d",
+		            iface, WEXITSTATUS (status));
+	} else if (WIFSTOPPED (status)) {
+		nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd stopped unexpectedly with signal %d",
+		            iface, WSTOPSIG (status));
+	} else if (WIFSIGNALED (status)) {
+		nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died with signal %d",
+		             iface, WTERMSIG (status));
+	} else {
+		nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died from an unknown cause", iface);
+	}
+
+	aipd_cleanup (self);
+
+	state = nm_device_get_state (self);
+	if (nm_device_is_activating (self) || (state == NM_DEVICE_STATE_ACTIVATED))
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_FAILED);
+}
+
+static gboolean
+aipd_timeout_cb (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!priv->aipd_timeout)
+		return FALSE;
+	priv->aipd_timeout = 0;
+
+	nm_log_info (LOGD_AUTOIP4, "(%s): avahi-autoipd timed out.", nm_device_get_iface (self));
+	aipd_cleanup (self);
+
+	if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
+		nm_device_activate_schedule_stage4_ip4_config_timeout (self);
+
+	return FALSE;
+}
+
+static void
+aipd_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point.
+	 * Give child it's own program group for signal
+	 * separation.
+	 */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static gboolean
+aipd_exec (NMDevice *self, GError **error)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	char *argv[5];
+	gboolean success = FALSE;
+	const char **aipd_binary = NULL;
+	static const char *aipd_paths[] = {
+		"/usr/sbin/avahi-autoipd",
+		"/usr/local/sbin/avahi-autoipd",
+		NULL
+	};
+
+	aipd_cleanup (self);
+
+	/* Find avahi-autoipd */
+	aipd_binary = aipd_paths;
+	while (*aipd_binary != NULL) {
+		if (g_file_test (*aipd_binary, G_FILE_TEST_EXISTS))
+			break;
+		aipd_binary++;
+	}
+
+	if (!*aipd_binary) {
+		g_set_error (error, 0, 0, "%s", "couldn't find avahi-autoipd");
+		return FALSE;
+	}
+
+	argv[0] = (char *) (*aipd_binary);
+	argv[1] = "--script";
+	argv[2] = LIBEXECDIR "/nm-avahi-autoipd.action";
+	argv[3] = (char *) nm_device_get_ip_iface (self);
+	argv[4] = NULL;
+
+	success = g_spawn_async ("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+	                         &aipd_child_setup, NULL, &(priv->aipd_pid), error);
+	if (!success)
+		return FALSE;
+
+	/* Monitor the child process so we know when it dies */
+	priv->aipd_watch = g_child_watch_add (priv->aipd_pid, aipd_watch_cb, self);
+
+	/* Start a timeout to bound the address attempt */
+	priv->aipd_timeout = g_timeout_add_seconds (20, aipd_timeout_cb, self);
+
+	return TRUE;
+}
+
+static void
+dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data),
+	                            (const char *) key,
+	                            (const char *) value);
+}
+
+static void
+dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data),
+	                            (const char *) key,
+	                            (const char *) value);
+}
+
+static void
+handle_dhcp_lease_change (NMDevice *device, gboolean ipv6)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMIP4Config *ip4_config;
+	NMSettingIP4Config *s_ip4;
+	NMIP6Config *ip6_config;
+	NMSettingIP6Config *s_ip6;
+	NMConnection *connection;
+	NMActRequest *req;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	gboolean assumed;
+
+	req = nm_device_get_act_request (device);
+	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+	assumed = nm_act_request_get_assumed (req);
+
+	if (ipv6) {
+		ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE);
+		if (!ip6_config) {
+			nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv6 config for rebind",
+			             nm_device_get_ip_iface (device));
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+			return;
+		}
+
+		s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG));
+		nm_utils_merge_ip6_config (ip6_config, s_ip6);
+
+		g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP6_CONFIG, ip6_config);
+
+		if (nm_device_set_ip6_config (device, ip6_config, assumed, &reason)) {
+			nm_dhcp6_config_reset (priv->dhcp6_config);
+			nm_dhcp_client_foreach_option (priv->dhcp6_client,
+			                               dhcp6_add_option_cb,
+			                               priv->dhcp6_config);
+		} else {
+			nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to DHCP event.",
+			             nm_device_get_ip_iface (device));
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		}
+	} else {
+		ip4_config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE);
+		if (!ip4_config) {
+			nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv4 config for rebind",
+			             nm_device_get_ip_iface (device));
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+			return;
+		}
+
+		s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG));
+		nm_utils_merge_ip4_config (ip4_config, s_ip4);
+
+		g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, ip4_config);
+
+		if (nm_device_set_ip4_config (device, ip4_config, assumed, &reason)) {
+			nm_dhcp4_config_reset (priv->dhcp4_config);
+			nm_dhcp_client_foreach_option (priv->dhcp4_client,
+			                               dhcp4_add_option_cb,
+			                               priv->dhcp4_config);
+		} else {
+			nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv4 config in response to DHCP event.",
+			             nm_device_get_ip_iface (device));
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		}
+	}
+}
+
+static void
+dhcp_state_changed (NMDHCPClient *client,
+					NMDHCPState state,
+					gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDeviceState dev_state;
+	gboolean ipv6;
+
+	ipv6 = nm_dhcp_client_get_ipv6 (client);
+	dev_state = nm_device_get_state (device);
+
+	if (ipv6) {
+		nm_log_dbg (LOGD_DHCP6, "(%s): new DHCPv6 client state %d",
+		            nm_device_get_iface (device), dev_state);
+	} else {
+		nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d",
+		            nm_device_get_iface (device), dev_state);
+	}
+
+	switch (state) {
+	case DHC_BOUND4:     /* lease obtained */
+	case DHC_BOUND6:
+	case DHC_RENEW4:     /* lease renewed */
+	case DHC_RENEW6:     /* lease renewed */
+	case DHC_REBOOT:     /* have valid lease, but now obtained a different one */
+	case DHC_REBIND4:    /* new, different lease */
+	case DHC_REBIND6:    /* new, different lease */
+		if (dev_state == NM_DEVICE_STATE_IP_CONFIG) {
+			if (ipv6)
+				nm_device_activate_schedule_stage4_ip6_config_get (device);
+			else
+				nm_device_activate_schedule_stage4_ip4_config_get (device);
+		} else if (dev_state == NM_DEVICE_STATE_ACTIVATED)
+			handle_dhcp_lease_change (device, ipv6);
+		break;
+	case DHC_TIMEOUT: /* timed out contacting DHCP server */
+		if (ipv6) {
+			nm_dhcp6_config_reset (priv->dhcp6_config);
+			if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
+				nm_device_activate_schedule_stage4_ip6_config_timeout (device);
+		} else {
+			nm_dhcp4_config_reset (priv->dhcp4_config);
+			if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
+				nm_device_activate_schedule_stage4_ip4_config_timeout (device);
+		}
+		break;
+	case DHC_END: /* dhclient exited normally */
+		/* In IPv6 info-only mode, the client doesn't handle leases so it
+		 * may exit right after getting a response from the server.  That's
+		 * normal.  In that case we just ignore the exit.
+		 */
+		if (ipv6 && (priv->dhcp6_mode == IP6_DHCP_OPT_OTHERCONF))
+			break;
+		/* Otherwise, fall through */
+	case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
+	case DHC_ABEND: /* dhclient exited abnormally */
+		if (ipv6)
+			nm_dhcp6_config_reset (priv->dhcp6_config);
+		else
+			nm_dhcp4_config_reset (priv->dhcp4_config);
+
+		/* dhclient quit and can't get/renew a lease; so kill the connection */
+		if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
+			if (ipv6)
+				nm_device_activate_schedule_stage4_ip6_config_timeout (device);
+			else
+				nm_device_activate_schedule_stage4_ip4_config_timeout (device);
+		} else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+		break;
+	case DHC_STOP:
+	case DHC_STOP6:
+	case DHC_EXPIRE:
+	case DHC_EXPIRE6:
+		if (dev_state == NM_DEVICE_STATE_ACTIVATED) {
+			if (ipv6)
+				nm_dhcp6_config_reset (priv->dhcp6_config);
+			else
+				nm_dhcp4_config_reset (priv->dhcp4_config);
+
+			/* dhclient quit and can't get/renew a lease; so kill the connection */
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+dhcp_timeout (NMDHCPClient *client, gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+
+	if (!nm_device_get_act_request (device))
+		return;
+
+	nm_dhcp_client_stop (client);
+
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
+		if (nm_dhcp_client_get_ipv6 (client))
+			nm_device_activate_schedule_stage4_ip6_config_timeout (device);
+		else
+			nm_device_activate_schedule_stage4_ip4_config_timeout (device);
+	}
+}
+
+static NMActStageReturn
+real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingIP4Config *s_ip4;
+	NMActRequest *req;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
+	const char *ip_iface, *method = NULL, *uuid;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	/* Use the IP interface (not the control interface) for IP stuff */
+	ip_iface = nm_device_get_ip_iface (self);
+
+	/* Make sure the interface is up before trying to do anything with it */
+	if (!nm_system_device_is_up_with_iface (ip_iface))
+		nm_system_device_set_up_down_with_iface (ip_iface, TRUE, NULL);
+
+	req = nm_device_get_act_request (self);
+	connection = nm_act_request_get_connection (req);
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
+	uuid = nm_setting_connection_get_uuid (s_con);
+
+	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+
+	/* If we did not receive IP4 configuration information, default to DHCP */
+	if (s_ip4)
+		method = nm_setting_ip4_config_get_method (s_ip4);
+
+	if (!s_ip4 || !method || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+		guint8 *anycast = NULL;
+
+		/* Begin a DHCP transaction on the interface */
+
+		if (priv->dhcp_anycast_address)
+			anycast = priv->dhcp_anycast_address->data;
+
+		/* Clear old exported DHCP options */
+		if (priv->dhcp4_config)
+			g_object_unref (priv->dhcp4_config);
+		priv->dhcp4_config = nm_dhcp4_config_new ();
+
+		priv->dhcp4_client = nm_dhcp_manager_start_ip4 (priv->dhcp_manager,
+		                                                ip_iface,
+		                                                uuid,
+		                                                s_ip4,
+		                                                priv->dhcp_timeout,
+		                                                anycast);
+		if (priv->dhcp4_client) {
+			priv->dhcp4_state_sigid = g_signal_connect (priv->dhcp4_client,
+			                                            "state-changed",
+			                                            G_CALLBACK (dhcp_state_changed),
+			                                            self);
+			priv->dhcp4_timeout_sigid = g_signal_connect (priv->dhcp4_client,
+			                                              "timeout",
+			                                              G_CALLBACK (dhcp_timeout),
+			                                              self);
+
+			/* DHCP devices will be notified by the DHCP manager when
+			 * stuff happens.	
+			 */
+			ret = NM_ACT_STAGE_RETURN_POSTPONE;
+		} else {
+			*reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED;
+			ret = NM_ACT_STAGE_RETURN_FAILURE;
+		}
+	} else if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
+		GError *error = NULL;
+		const char *iface = nm_device_get_iface (self);
+
+		/* Start avahi-autoipd */
+		if (aipd_exec (self, &error)) {
+			nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4,
+			             "Activation (%s) Stage 3 of 5 (IP Configure Start) started"
+			             " avahi-autoipd...", iface);
+			ret = NM_ACT_STAGE_RETURN_POSTPONE;
+		} else {
+			nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4,
+			             "Activation (%s) Stage 3 of 5 (IP Configure Start) failed"
+			             " to start avahi-autoipd: %s", iface, error->message);
+			g_error_free (error);
+			aipd_cleanup (self);
+			*reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED;
+			ret = NM_ACT_STAGE_RETURN_FAILURE;
+		}
+	} else if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
+		/* Nothing to do... */
+		priv->ip4_ready = TRUE;
+		ret = NM_ACT_STAGE_RETURN_STOP;
+	}
+
+	return ret;
+}
+
+static NMActStageReturn
+dhcp6_start (NMDevice *self,
+             NMConnection *connection,
+             guint32 dhcp_opt,
+             NMDeviceStateReason *reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	guint8 *anycast = NULL;
+	NMSettingIP6Config *s_ip6;
+	NMSettingConnection *s_con;
+	const char *uuid;
+	const char *ip_iface;
+
+	if (!connection) {
+		NMActRequest *req;
+
+		req = nm_device_get_act_request (self);
+		g_assert (req);
+		connection = nm_act_request_get_connection (req);
+		g_assert (connection);
+	}
+
+	/* Begin a DHCP transaction on the interface */
+
+	if (priv->dhcp_anycast_address)
+		anycast = priv->dhcp_anycast_address->data;
+
+	/* Clear old exported DHCP options */
+	if (priv->dhcp6_config)
+		g_object_unref (priv->dhcp6_config);
+	priv->dhcp6_config = nm_dhcp6_config_new ();
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
+	uuid = nm_setting_connection_get_uuid (s_con);
+
+	s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+
+	ip_iface = nm_device_get_ip_iface (self);
+	priv->dhcp6_client = nm_dhcp_manager_start_ip6 (priv->dhcp_manager,
+	                                                ip_iface,
+	                                                uuid,
+	                                                s_ip6,
+	                                                priv->dhcp_timeout,
+	                                                anycast,
+	                                                (dhcp_opt == IP6_DHCP_OPT_OTHERCONF) ? TRUE : FALSE);
+	if (priv->dhcp6_client) {
+		priv->dhcp6_state_sigid = g_signal_connect (priv->dhcp6_client,
+		                                            "state-changed",
+		                                            G_CALLBACK (dhcp_state_changed),
+		                                            self);
+		priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client,
+		                                              "timeout",
+		                                              G_CALLBACK (dhcp_timeout),
+		                                              self);
+
+		/* DHCP devices will be notified by the DHCP manager when stuff happens */
+		ret = NM_ACT_STAGE_RETURN_POSTPONE;
+	} else {
+		*reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED;
+		ret = NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	return ret;
+}
+
+static NMActStageReturn
+real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const char *ip_iface;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	NMActRequest *req;
+	NMConnection *connection;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	req = nm_device_get_act_request (self);
+	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	ip_iface = nm_device_get_ip_iface (self);
+
+	update_accept_ra_save (self);
+
+	priv->dhcp6_mode = IP6_DHCP_OPT_NONE;
+
+	if (   ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
+	    || ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
+		if (!addrconf6_setup (self)) {
+			*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+			goto out;
+		}
+		nm_ip6_manager_begin_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self));
+		ret = NM_ACT_STAGE_RETURN_POSTPONE;
+	} else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
+		/* reset the saved RA value when ipv6 is ignored */
+		if (priv->ip6_accept_ra_path) {
+			nm_utils_do_sysctl (priv->ip6_accept_ra_path,
+			                    priv->ip6_accept_ra_save ? "1\n" : "0\n");
+		}
+
+		priv->ip6_ready = TRUE;
+		ret = NM_ACT_STAGE_RETURN_STOP;
+	} else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		/* Router advertisements shouldn't be used in manual mode */
+		if (priv->ip6_accept_ra_path)
+			nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0\n");
+		ret = NM_ACT_STAGE_RETURN_SUCCESS;
+	}
+
+	/* Other methods (shared) aren't implemented yet */
+
+out:
+	return ret;
+}
+
+
+/*
+ * nm_device_activate_stage3_ip_config_start
+ *
+ * Begin automatic/manual IP configuration
+ *
+ */
+static gboolean
+nm_device_activate_stage3_ip_config_start (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	const char *iface;
+	NMActStageReturn ret;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	/* Clear the activation source ID now that this stage has run */
+	activation_source_clear (self, FALSE, 0);
+
+	iface = nm_device_get_iface (self);
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface);
+	nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
+
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_SUCCESS)
+		nm_device_activate_schedule_stage4_ip4_config_get (self);
+	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	} else if (ret == NM_ACT_STAGE_RETURN_STOP) {
+		/* Nothing to do */
+	} else
+		g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE);
+
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_SUCCESS)
+		nm_device_activate_schedule_stage4_ip6_config_get (self);
+	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	} else if (ret == NM_ACT_STAGE_RETURN_STOP) {
+		/* Nothing to do */
+	} else
+		g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE);
+
+out:
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) complete.", iface);
+	return FALSE;
+}
+
+
+/*
+ * nm_device_activate_schedule_stage3_ip_config_start
+ *
+ * Schedule IP configuration start
+ */
+void
+nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->act_request);
+
+	activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0);
+
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) scheduled.",
+	             nm_device_get_iface (self));
+}
+
+static GHashTable *shared_ips = NULL;
+
+static void
+release_shared_ip (gpointer data)
+{
+	g_hash_table_remove (shared_ips, data);
+}
+
+static guint32
+reserve_shared_ip (void)
+{
+	guint32 start = (guint32) ntohl (0x0a2a2b01); /* 10.42.43.1 */
+	guint32 count = 0;
+
+	while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) {
+		count += ntohl (0x100);
+		if (count > ntohl (0xFE00)) {
+			nm_log_err (LOGD_SHARING, "ran out of shared IP addresses!");
+			return 0;
+		}
+	}
+
+	g_hash_table_insert (shared_ips, GUINT_TO_POINTER (start + count), GUINT_TO_POINTER (TRUE));
+	return start + count;
+}
+
+static NMIP4Config *
+nm_device_new_ip4_shared_config (NMDevice *self, NMDeviceStateReason *reason)
+{
+	NMIP4Config *config = NULL;
+	NMIP4Address *addr;
+	guint32 tmp_addr;
+
+	g_return_val_if_fail (self != NULL, NULL);
+
+	if (G_UNLIKELY (shared_ips == NULL))
+		shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+	tmp_addr = reserve_shared_ip ();
+	if (!tmp_addr) {
+		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		return NULL;
+	}
+
+	config = nm_ip4_config_new ();
+	addr = nm_ip4_address_new ();
+	nm_ip4_address_set_address (addr, tmp_addr);
+	nm_ip4_address_set_prefix (addr, 24);
+	nm_ip4_config_take_address (config, addr);
+
+	/* Remove the address lock when the object gets disposed */
+	g_object_set_data_full (G_OBJECT (config), "shared-ip",
+	                        GUINT_TO_POINTER (tmp_addr), release_shared_ip);
+
+	return config;
+}
+
+static NMActStageReturn
+real_act_stage4_get_ip4_config (NMDevice *self,
+                                NMIP4Config **config,
+                                NMDeviceStateReason *reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	NMConnection *connection;
+	NMSettingIP4Config *s_ip4;
+	const char *ip_iface;
+
+	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	/* Use the IP interface (not the control interface) for IP stuff */
+	ip_iface = nm_device_get_ip_iface (self);
+
+	connection = nm_act_request_get_connection (nm_device_get_act_request (self));
+	g_assert (connection);
+
+	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+
+	if (priv->dhcp4_client) {
+		/* DHCP */
+		*config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE);
+		if (*config) {
+			/* Merge user-defined overrides into the IP4Config to be applied */
+			nm_utils_merge_ip4_config (*config, s_ip4);
+
+			nm_dhcp4_config_reset (priv->dhcp4_config);
+			nm_dhcp_client_foreach_option (priv->dhcp4_client,
+			                               dhcp4_add_option_cb,
+			                               priv->dhcp4_config);
+
+			/* Notify of new DHCP4 config */
+			g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
+		} else
+			*reason = NM_DEVICE_STATE_REASON_DHCP_ERROR;
+	} else {
+		/* Not DHCP */
+		const char *method;
+
+		g_assert (s_ip4);
+
+		method = nm_setting_ip4_config_get_method (s_ip4);
+		g_assert (method);
+
+		if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
+			*config = aipd_get_ip4_config (self, reason);
+		} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+			*config = nm_ip4_config_new ();
+			if (*config) {
+				/* Merge user-defined overrides into the IP4Config to be applied */
+				nm_utils_merge_ip4_config (*config, s_ip4);
+			} else
+				*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
+			*config = nm_device_new_ip4_shared_config (self, reason);
+			if (*config)
+				priv->dnsmasq_manager = nm_dnsmasq_manager_new (ip_iface);
+		} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
+			ret = NM_ACT_STAGE_RETURN_SUCCESS;
+	}
+
+	if (!*config) {
+		/* Make sure device is up even if config fails */
+		nm_device_bring_up (self, FALSE, NULL);
+	} else
+		ret = NM_ACT_STAGE_RETURN_SUCCESS;
+
+	return ret;
+}
+
+/*
+ * nm_device_activate_stage4_ip4_config_get
+ *
+ * Retrieve the correct IPv4 config.
+ *
+ */
+static gboolean
+nm_device_activate_stage4_ip4_config_get (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMIP4Config *ip4_config = NULL;
+	NMActStageReturn ret;
+	const char *iface = NULL;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	/* Clear the activation source ID now that this stage has run */
+	activation_source_clear (self, FALSE, AF_INET);
+
+	iface = nm_device_get_iface (self);
+	nm_log_info (LOGD_DEVICE | LOGD_IP4,
+	             "Activation (%s) Stage 4 of 5 (IP4 Configure Get) started...",
+	             iface);
+
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_get_ip4_config (self, &ip4_config, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
+		goto out;
+	else if (!ip4_config || (ret == NM_ACT_STAGE_RETURN_FAILURE)) {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	}
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);	
+
+	g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
+					   NM_ACT_REQUEST_IP4_CONFIG, ip4_config);
+
+	nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET);
+
+out:
+	nm_log_info (LOGD_DEVICE | LOGD_IP4,
+	             "Activation (%s) Stage 4 of 5 (IP4 Configure Get) complete.",
+	             iface);
+	return FALSE;
+}
+
+
+/*
+ * nm_device_activate_schedule_stage4_ip4_config_get
+ *
+ * Schedule creation of the IPv4 config
+ *
+ */
+void
+nm_device_activate_schedule_stage4_ip4_config_get (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->act_request);
+
+	activation_source_schedule (self, nm_device_activate_stage4_ip4_config_get, AF_INET);
+
+	nm_log_info (LOGD_DEVICE | LOGD_IP4,
+	             "Activation (%s) Stage 4 of 5 (IP4 Configure Get) scheduled...",
+	             nm_device_get_iface (self));
+}
+
+
+static NMActStageReturn
+real_act_stage4_ip4_config_timeout (NMDevice *self,
+									NMIP4Config **config,
+									NMDeviceStateReason *reason)
+{
+	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	/* Notify of invalid DHCP4 config object */
+	g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
+
+	if (nm_device_ip_config_should_fail (self, FALSE)) {
+		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+
+/*
+ * nm_device_activate_stage4_ip4_config_timeout
+ *
+ * Time out on retrieving the IPv4 config.
+ *
+ */
+static gboolean
+nm_device_activate_stage4_ip4_config_timeout (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMIP4Config *ip4_config = NULL;
+	const char *iface;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	/* Clear the activation source ID now that this stage has run */
+	activation_source_clear (self, FALSE, AF_INET);
+
+	iface = nm_device_get_iface (self);
+	nm_log_info (LOGD_DEVICE | LOGD_IP4,
+	             "Activation (%s) Stage 4 of 5 (IP4 Configure Timeout) started...",
+	             iface);
+
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &ip4_config, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
+		goto out;
+	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	}
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);	
+
+	if (ip4_config) {
+		g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
+						   NM_ACT_REQUEST_IP4_CONFIG, ip4_config);
+	}
+
+	nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET);
+
+out:
+	nm_log_info (LOGD_DEVICE | LOGD_IP4,
+	             "Activation (%s) Stage 4 of 5 (IP4 Configure Timeout) complete.",
+	             iface);
+	return FALSE;
+}
+
+
+/*
+ * nm_device_activate_schedule_stage4_ip4_config_timeout
+ *
+ * Deal with a timeout of the IPv4 configuration
+ *
+ */
+void
+nm_device_activate_schedule_stage4_ip4_config_timeout (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->act_request);
+
+	activation_source_schedule (self, nm_device_activate_stage4_ip4_config_timeout, AF_INET);
+
+	nm_log_info (LOGD_DEVICE | LOGD_IP4,
+	             "Activation (%s) Stage 4 of 5 (IP4 Configure Timeout) scheduled...",
+	             nm_device_get_iface (self));
+}
+
+static void
+merge_dhcp_config_to_master (NMIP6Config *dst, NMIP6Config *src)
+{
+	guint32 i;
+
+	g_return_if_fail (src != NULL);
+	g_return_if_fail (dst != NULL);
+
+	/* addresses */
+	for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++)
+		nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i));
+
+	/* ptp address; only replace if src doesn't have one */
+	if (!nm_ip6_config_get_ptp_address (dst))
+		nm_ip6_config_set_ptp_address (dst, nm_ip6_config_get_ptp_address (src));
+
+	/* nameservers */
+	for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++)
+		nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i));
+
+	/* routes */
+	for (i = 0; i < nm_ip6_config_get_num_routes (src); i++)
+		nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i));
+
+	/* domains */
+	for (i = 0; i < nm_ip6_config_get_num_domains (src); i++)
+		nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
+
+	/* dns searches */
+	for (i = 0; i < nm_ip6_config_get_num_searches (src); i++)
+		nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
+
+	if (!nm_ip6_config_get_mss (dst))
+		nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src));
+}
+
+static NMActStageReturn
+real_act_stage4_get_ip6_config (NMDevice *self,
+                                NMIP6Config **config,
+                                NMDeviceStateReason *reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+	NMSettingIP6Config *s_ip6;
+	const char *ip_iface;
+
+	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	/* Use the IP interface (not the control interface) for IP stuff */
+	ip_iface = nm_device_get_ip_iface (self);
+
+	connection = nm_act_request_get_connection (nm_device_get_act_request (self));
+	g_assert (connection);
+
+	if (   ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
+	    || ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
+		*config = nm_ip6_manager_get_ip6_config (priv->ip6_manager,
+		                                         nm_device_get_ip_ifindex (self));
+		if (!*config) {
+			*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+			goto out;
+		}
+	} else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		*config = nm_ip6_config_new ();
+		if (!*config) {
+			*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+			goto out;
+		}
+	}
+
+	/* Autoconf might have triggered DHCPv6 too */
+	if (priv->dhcp6_client) {
+		NMIP6Config *dhcp;
+
+		dhcp = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE);
+		if (!dhcp) {
+			*reason = NM_DEVICE_STATE_REASON_DHCP_ERROR;
+			goto out;
+		}
+
+		/* For "managed" and DHCP-only setups, we use only the DHCP-supplied
+		 * IPv6 config.  But when autoconf is enabled, we have to merge the
+		 * autoconf config and the DHCP-supplied config, then merge the
+		 * user's overrides from the connection to get the final configuration
+		 * that gets applied to the device.
+		 */
+		if (*config) {
+			/* Merge autoconf and DHCP configs */
+			merge_dhcp_config_to_master (*config, dhcp);
+			g_object_unref (dhcp);
+			dhcp = NULL;
+		} else {
+			*config = dhcp;
+		}
+
+		/* Copy the new DHCPv6 configuration into the DHCP config object that's
+		 * exported over D-Bus to clients.
+		 */
+		nm_dhcp6_config_reset (priv->dhcp6_config);
+		nm_dhcp_client_foreach_option (priv->dhcp6_client,
+		                               dhcp6_add_option_cb,
+		                               priv->dhcp6_config);
+
+		/* Notify of new DHCP6 config */
+		g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG);
+	}
+
+	/* Merge user-defined overrides into the IP6Config to be applied */
+	if (*config) {
+		s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+		nm_utils_merge_ip6_config (*config, s_ip6);
+	}
+
+out:
+	return *config ? NM_ACT_STAGE_RETURN_SUCCESS : NM_ACT_STAGE_RETURN_FAILURE;
+}
+
+/*
+ * nm_device_activate_stage4_ip6_config_get
+ *
+ * Retrieve the correct IPv6 config.
+ *
+ */
+static gboolean
+nm_device_activate_stage4_ip6_config_get (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMIP6Config *ip6_config = NULL;
+	NMActStageReturn ret;
+	const char *iface = NULL;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	/* Clear the activation source ID now that this stage has run */
+	activation_source_clear (self, FALSE, AF_INET6);
+
+	iface = nm_device_get_iface (self);
+	nm_log_info (LOGD_DEVICE | LOGD_IP6,
+	             "Activation (%s) Stage 4 of 5 (IP6 Configure Get) started...",
+	             iface);
+
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_get_ip6_config (self, &ip6_config, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
+		goto out;
+	else if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+	{
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	}
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+
+	g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
+					   NM_ACT_REQUEST_IP6_CONFIG, ip6_config);
+
+	nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET6);
+
+out:
+	nm_log_info (LOGD_DEVICE | LOGD_IP6,
+	             "Activation (%s) Stage 4 of 5 (IP6 Configure Get) complete.",
+	             iface);
+	return FALSE;
+}
+
+
+/*
+ * nm_device_activate_schedule_stage4_ip6_config_get
+ *
+ * Schedule creation of the IPv6 config
+ *
+ */
+void
+nm_device_activate_schedule_stage4_ip6_config_get (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->act_request);
+
+	activation_source_schedule (self, nm_device_activate_stage4_ip6_config_get, AF_INET6);
+
+	nm_log_info (LOGD_DEVICE | LOGD_IP6,
+	             "Activation (%s) Stage 4 of 5 (IP6 Configure Get) scheduled...",
+	             nm_device_get_iface (self));
+}
+
+
+static NMActStageReturn
+real_act_stage4_ip6_config_timeout (NMDevice *self,
+									NMIP6Config **config,
+									NMDeviceStateReason *reason)
+{
+	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	/* Notify of invalid DHCP4 config object */
+	g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG);
+
+	if (nm_device_ip_config_should_fail (self, TRUE)) {
+		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+
+/*
+ * nm_device_activate_stage4_ip6_config_timeout
+ *
+ * Time out on retrieving the IPv6 config.
+ *
+ */
+static gboolean
+nm_device_activate_stage4_ip6_config_timeout (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMIP6Config *ip6_config = NULL;
+	const char *iface;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	/* Clear the activation source ID now that this stage has run */
+	activation_source_clear (self, FALSE, AF_INET6);
+
+	iface = nm_device_get_iface (self);
+	nm_log_info (LOGD_DEVICE | LOGD_IP6,
+	             "Activation (%s) Stage 4 of 5 (IP6 Configure Timeout) started...",
+	             iface);
+
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &ip6_config, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
+		goto out;
+	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	}
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+
+	if (ip6_config) {
+		g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
+						   NM_ACT_REQUEST_IP6_CONFIG, ip6_config);
+	}
+
+	nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET6);
+
+out:
+	nm_log_info (LOGD_DEVICE | LOGD_IP6,
+	             "Activation (%s) Stage 4 of 5 (IP6 Configure Timeout) complete.",
+	             iface);
+	return FALSE;
+}
+
+
+/*
+ * nm_device_activate_schedule_stage4_ip6_config_timeout
+ *
+ * Deal with a timeout of the IPv6 configuration
+ *
+ */
+void
+nm_device_activate_schedule_stage4_ip6_config_timeout (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->act_request);
+
+	activation_source_schedule (self, nm_device_activate_stage4_ip6_config_timeout, AF_INET6);
+
+	nm_log_info (LOGD_DEVICE | LOGD_IP6,
+	             "Activation (%s) Stage 4 of 5 (IP6 Configure Timeout) scheduled...",
+	             nm_device_get_iface (self));
+}
+
+static void
+share_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static gboolean
+share_init (void)
+{
+	int status;
+	char *modules[] = { "ip_tables", "iptable_nat", "nf_nat_ftp", "nf_nat_irc",
+	                    "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323",
+	                    NULL };
+	char **iter;
+
+	if (!nm_utils_do_sysctl ("/proc/sys/net/ipv4/ip_forward", "1\n")) {
+		nm_log_err (LOGD_SHARING, "Error starting IP forwarding: (%d) %s",
+					errno, strerror (errno));
+		return FALSE;
+	}
+
+	if (!nm_utils_do_sysctl ("/proc/sys/net/ipv4/ip_dynaddr", "1\n")) {
+		nm_log_err (LOGD_SHARING, "error starting IP forwarding: (%d) %s",
+					errno, strerror (errno));
+	}
+
+	for (iter = modules; *iter; iter++) {
+		char *argv[3] = { "/sbin/modprobe", *iter, NULL };
+		char *envp[1] = { NULL };
+		GError *error = NULL;
+
+		if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+		                   share_child_setup, NULL, NULL, NULL, &status, &error)) {
+			nm_log_err (LOGD_SHARING, "error loading NAT module %s: (%d) %s",
+			            *iter, error ? error->code : 0,
+			            (error && error->message) ? error->message : "unknown");
+			if (error)
+				g_error_free (error);
+		}
+	}
+
+	return TRUE;
+}
+
+static void
+add_share_rule (NMActRequest *req, const char *table, const char *fmt, ...)
+{
+	va_list args;
+	char *cmd;
+
+	va_start (args, fmt);
+	cmd = g_strdup_vprintf (fmt, args);
+	va_end (args);
+
+	nm_act_request_add_share_rule (req, table, cmd);
+	g_free (cmd);
+}
+
+static gboolean
+start_sharing (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActRequest *req;
+	GError *error = NULL;
+	char str_addr[INET_ADDRSTRLEN + 1];
+	char str_mask[INET_ADDRSTRLEN + 1];
+	guint32 netmask, network;
+	NMIP4Config *ip4_config;
+	NMIP4Address *ip4_addr;
+	const char *ip_iface;
+
+	ip_iface = nm_device_get_ip_iface (self);
+
+	ip4_config = nm_device_get_ip4_config (self);
+	if (!ip4_config)
+		return FALSE;
+
+	ip4_addr = nm_ip4_config_get_address (ip4_config, 0);
+	if (!ip4_addr || !nm_ip4_address_get_address (ip4_addr))
+		return FALSE;
+
+	netmask = nm_utils_ip4_prefix_to_netmask (nm_ip4_address_get_prefix (ip4_addr));
+	if (!inet_ntop (AF_INET, &netmask, str_mask, sizeof (str_mask)))
+		return FALSE;
+
+	network = nm_ip4_address_get_address (ip4_addr) & netmask;
+	if (!inet_ntop (AF_INET, &network, str_addr, sizeof (str_addr)))
+		return FALSE;
+
+	if (!share_init ())
+		return FALSE;
+
+	req = nm_device_get_act_request (self);
+	g_assert (req);
+
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT", ip_iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT", ip_iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT", ip_iface);
+	add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT", ip_iface);
+	add_share_rule (req, "filter", "FORWARD --in-interface %s --jump REJECT", ip_iface);
+	add_share_rule (req, "filter", "FORWARD --out-interface %s --jump REJECT", ip_iface);
+	add_share_rule (req, "filter", "FORWARD --in-interface %s --out-interface %s --jump ACCEPT", ip_iface, ip_iface);
+	add_share_rule (req, "filter", "FORWARD --source %s/%s --in-interface %s --jump ACCEPT", str_addr, str_mask, ip_iface);
+	add_share_rule (req, "filter", "FORWARD --destination %s/%s --out-interface %s --match state --state ESTABLISHED,RELATED --jump ACCEPT", str_addr, str_mask, ip_iface);
+	add_share_rule (req, "nat", "POSTROUTING --source %s/%s --destination ! %s/%s --jump MASQUERADE", str_addr, str_mask, str_addr, str_mask);
+
+	nm_act_request_set_shared (req, TRUE);
+
+	if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, ip4_config, &error)) {
+		nm_log_err (LOGD_SHARING, "(%s/%s): failed to start dnsmasq: %s",
+		            nm_device_get_iface (self), ip_iface, error->message);
+		g_error_free (error);
+		nm_act_request_set_shared (req, FALSE);
+		return FALSE;
+	}
+
+	priv->dnsmasq_state_id = g_signal_connect (priv->dnsmasq_manager, "state-changed",
+	                                           G_CALLBACK (dnsmasq_state_changed_cb),
+	                                           self);
+	return TRUE;
+}
+
+/*
+ * nm_device_activate_stage5_ip_config_commit
+ *
+ * Commit the IP config on the device
+ *
+ */
+static gboolean
+nm_device_activate_stage5_ip_config_commit (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActRequest *act_request;
+	NMIP4Config *ip4_config = NULL;
+	NMIP6Config *ip6_config = NULL;
+	const char *iface, *method = NULL;
+	NMConnection *connection;
+	NMSettingIP4Config *s_ip4;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	gboolean assumed;
+
+	/* Get the new IP4 and IP6 configs; since this stage gets rerun
+	 * when automatic configuration changes (DHCP lease renewal, new
+	 * IPv6 router advertisement, etc), it's possible that only one of
+	 * them will be set.
+	 */
+	act_request = nm_device_get_act_request (self);
+
+	ip4_config = g_object_get_data (G_OBJECT (act_request),
+									NM_ACT_REQUEST_IP4_CONFIG);
+	g_object_set_data (G_OBJECT (act_request),
+					   NM_ACT_REQUEST_IP4_CONFIG, NULL);
+
+	ip6_config = g_object_get_data (G_OBJECT (act_request),
+									NM_ACT_REQUEST_IP6_CONFIG);
+	g_object_set_data (G_OBJECT (act_request),
+					   NM_ACT_REQUEST_IP6_CONFIG, NULL);
+
+	/* Clear the activation source ID now that this stage has run */
+	activation_source_clear (self, FALSE, 0);
+
+	iface = nm_device_get_iface (self);
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IP Configure Commit) started...",
+	             iface);
+
+	assumed = nm_act_request_get_assumed (priv->act_request);
+
+	if (!ip6_config && !ip4_config) {
+		nm_log_info (LOGD_DEVICE,
+		             "Activation (%s) Stage 5 of 5 (IP Configure Commit) failed (no IP configuration found)",
+				     iface);
+		nm_device_state_changed (self,
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		goto out;
+	}
+
+	if (ip4_config && !nm_device_set_ip4_config (self, ip4_config, assumed, &reason)) {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	}
+
+	if (ip6_config && !nm_device_set_ip6_config (self, ip6_config, assumed, &reason)) {
+		nm_log_info (LOGD_DEVICE | LOGD_IP6,
+		             "Activation (%s) Stage 5 of 5 (IP Configure Commit) IPv6 failed",
+				     iface);
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		goto out;
+	}
+
+	connection = nm_act_request_get_connection (nm_device_get_act_request (self));
+
+	if (ip4_config) {
+		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+		if (s_ip4)
+			method = nm_setting_ip4_config_get_method (s_ip4);
+
+		if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
+			if (!start_sharing (self)) {
+				nm_log_warn (LOGD_SHARING, "Activation (%s) Stage 5 of 5 (IP Configure Commit) start sharing failed.", iface);
+				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
+				goto out;
+			}
+		}
+	}
+
+	nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
+
+out:
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IP Configure Commit) complete.",
+	             iface);
+
+	/* Balance IP config creation; device takes ownership in set_ip*_config() */
+	if (ip4_config)
+		g_object_unref (ip4_config);
+	if (ip6_config)
+		g_object_unref (ip6_config);
+
+	return FALSE;
+}
+
+
+/*
+ * nm_device_activate_schedule_stage5_ip_config_commit
+ *
+ * Schedule commit of the IP config
+ */
+static void
+nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self, int family)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->act_request);
+
+	if (family == AF_INET)
+		priv->ip4_ready = TRUE;
+	else if (family == AF_INET6)
+		priv->ip6_ready = TRUE;
+
+	/* Note that these are only set FALSE at stage3, so once you've
+	 * made it all the way through activation once, you can jump back
+	 * into stage4 (eg, for a DHCP lease change) and not worry about
+	 * needing both IPv4 and IPv6 to complete.
+	 */
+	if (!priv->ip4_ready || !priv->ip6_ready)
+		return;
+
+	activation_source_schedule (self, nm_device_activate_stage5_ip_config_commit, 0);
+
+	nm_log_info (LOGD_DEVICE,
+	             "Activation (%s) Stage 5 of 5 (IP Configure Commit) scheduled...",
+	             nm_device_get_iface (self));
+}
+
+
+static void
+clear_act_request (NMDevice *self)
+{
+	NMDevicePrivate * priv;
+
+	g_return_if_fail (self != NULL);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!priv->act_request)
+		return;
+
+	if (priv->secrets_updated_id) {
+		g_signal_handler_disconnect (priv->act_request,
+		                             priv->secrets_updated_id);
+		priv->secrets_updated_id = 0;
+	}
+
+	if (priv->secrets_failed_id) {
+		g_signal_handler_disconnect (priv->act_request,
+		                             priv->secrets_failed_id);
+		priv->secrets_failed_id = 0;
+	}
+
+	nm_act_request_set_default (priv->act_request, FALSE);
+
+	g_object_unref (priv->act_request);
+	priv->act_request = NULL;
+}
+
+static void
+delayed_transitions_clear (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->failed_to_disconnected_id) {
+		nm_log_dbg (LOGD_DEVICE, "(%s): clearing failed->disconnected transition",
+		            nm_device_get_iface (self));
+		g_source_remove (priv->failed_to_disconnected_id);
+		priv->failed_to_disconnected_id = 0;
+	}
+	if (priv->unavailable_to_disconnected_id) {
+		nm_log_dbg (LOGD_DEVICE, "(%s): clearing unavailable->disconnected transition",
+		            nm_device_get_iface (self));
+		g_source_remove (priv->unavailable_to_disconnected_id);
+		priv->unavailable_to_disconnected_id = 0;
+	}
+}
+
+static void
+dhcp4_cleanup (NMDevice *self, gboolean stop)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->dhcp4_config) {
+		g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
+		g_object_unref (priv->dhcp4_config);
+		priv->dhcp4_config = NULL;
+	}
+
+	if (priv->dhcp4_client) {
+		/* Stop any ongoing DHCP transaction on this device */
+		if (priv->dhcp4_state_sigid) {
+			g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid);
+			priv->dhcp4_state_sigid = 0;
+		}
+
+		if (priv->dhcp4_timeout_sigid) {
+			g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid);
+			priv->dhcp4_timeout_sigid = 0;
+		}
+
+		if (stop)
+			nm_dhcp_client_stop (priv->dhcp4_client);
+
+		g_object_unref (priv->dhcp4_client);
+		priv->dhcp4_client = NULL;
+	}
+}
+
+static void
+dhcp6_cleanup (NMDevice *self, gboolean stop)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	priv->dhcp6_mode = IP6_DHCP_OPT_NONE;
+
+	if (priv->dhcp6_config) {
+		g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG);
+		g_object_unref (priv->dhcp6_config);
+		priv->dhcp6_config = NULL;
+	}
+
+	if (priv->dhcp6_client) {
+		if (priv->dhcp6_state_sigid) {
+			g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid);
+			priv->dhcp6_state_sigid = 0;
+		}
+
+		if (priv->dhcp6_timeout_sigid) {
+			g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid);
+			priv->dhcp6_timeout_sigid = 0;
+		}
+
+		if (stop)
+			nm_dhcp_client_stop (priv->dhcp6_client);
+
+		g_object_unref (priv->dhcp6_client);
+		priv->dhcp6_client = NULL;
+	}
+}
+
+static void
+dnsmasq_cleanup (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!priv->dnsmasq_manager)
+		return;
+
+	if (priv->dnsmasq_state_id) {
+		g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id);
+		priv->dnsmasq_state_id = 0;
+	}
+
+	nm_dnsmasq_manager_stop (priv->dnsmasq_manager);
+	g_object_unref (priv->dnsmasq_manager);
+	priv->dnsmasq_manager = NULL;
+}
+
+/*
+ * nm_device_deactivate_quickly
+ *
+ * Quickly deactivate a device, for things like sleep, etc.  Doesn't
+ * clean much stuff up, and nm_device_deactivate() should be called
+ * on the device eventually.
+ *
+ */
+gboolean
+nm_device_deactivate_quickly (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	/* Break the activation chain */
+	activation_source_clear (self, TRUE, AF_INET);
+	activation_source_clear (self, TRUE, AF_INET6);
+
+	/* Clear any delayed transitions */
+	delayed_transitions_clear (self);
+
+	dhcp4_cleanup (self, TRUE);
+	dhcp6_cleanup (self, TRUE);
+	addrconf6_cleanup (self);
+	dnsmasq_cleanup (self);
+	aipd_cleanup (self);
+
+	/* Turn off router advertisements until they are needed */
+	if (priv->ip6_accept_ra_path)
+		nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0\n");
+
+	/* Call device type-specific deactivation */
+	if (NM_DEVICE_GET_CLASS (self)->deactivate_quickly)
+		NM_DEVICE_GET_CLASS (self)->deactivate_quickly (self);
+
+	/* Tear down an existing activation request */
+	clear_act_request (self);
+
+	return TRUE;
+}
+
+/*
+ * nm_device_deactivate
+ *
+ * Remove a device's routing table entries and IP address.
+ *
+ */
+static void
+nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason)
+{
+	NMDevice *self = NM_DEVICE (device);
+	NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
+
+	g_return_if_fail (self != NULL);
+
+	nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason: %d).",
+	             nm_device_get_iface (self), reason);
+
+	nm_device_deactivate_quickly (self);
+
+	/* Take out any entries in the routing table and any IP address the device had. */
+	nm_system_device_flush_routes (self, nm_device_get_ip6_config (self) ? AF_UNSPEC : AF_INET);
+	nm_system_device_flush_addresses (self);
+	nm_device_update_ip4_address (self);	
+
+	/* Clean up nameservers and addresses */
+	nm_device_set_ip4_config (self, NULL, FALSE, &ignored);
+	nm_device_set_ip6_config (self, NULL, FALSE, &ignored);
+
+	/* Call device type-specific deactivation */
+	if (NM_DEVICE_GET_CLASS (self)->deactivate)
+		NM_DEVICE_GET_CLASS (self)->deactivate (self);
+}
+
+static gboolean
+device_disconnect (NMDeviceInterface *device,
+                   GError **error)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (NM_DEVICE (device));
+
+	priv->autoconnect_inhibit = TRUE;	
+	nm_device_state_changed (NM_DEVICE (device), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_USER_REQUESTED);
+	return TRUE;
+}
+
+static gboolean
+check_connection_compatible (NMDeviceInterface *dev_iface,
+                             NMConnection *connection,
+                             GError **error)
+{
+	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (NM_DEVICE (dev_iface));
+
+	if (klass->check_connection_compatible)
+		return klass->check_connection_compatible (NM_DEVICE (dev_iface), connection, error);
+
+	return TRUE;
+}
+
+static void
+connection_secrets_updated_cb (NMActRequest *req,
+                               NMConnection *connection,
+                               GSList *updated_settings,
+                               RequestSecretsCaller caller,
+                               gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+
+	if (NM_DEVICE_GET_CLASS (self)->connection_secrets_updated)
+		NM_DEVICE_GET_CLASS (self)->connection_secrets_updated (self, connection, updated_settings, caller);
+}
+
+static void
+connection_secrets_failed_cb (NMActRequest *req,
+                              NMConnection *connection,
+                              const char *setting_name,
+                              RequestSecretsCaller caller,
+                              gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+
+	nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS);
+}
+
+static gboolean
+device_activation_precheck (NMDevice *self, NMConnection *connection, GError **error)
+{
+	NMConnection *current_connection;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+	if (nm_device_get_state (self) != NM_DEVICE_STATE_ACTIVATED)
+		return TRUE;
+
+	if (!nm_device_is_activating (self))
+		return TRUE;
+
+	// FIXME: why not just check connection path & service?
+	current_connection = nm_act_request_get_connection (nm_device_get_act_request (self));
+	if (nm_connection_compare (connection, current_connection, NM_SETTING_COMPARE_FLAG_EXACT)) {
+		/* Already activating or activated with the same connection */
+		g_set_error (error,
+		             NM_DEVICE_INTERFACE_ERROR,
+		             NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING,
+		             "%s", "Connection is already activating");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+nm_device_activate (NMDeviceInterface *device,
+                    NMActRequest *req,
+                    GError **error)
+{
+	NMDevice *self = NM_DEVICE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!device_activation_precheck (self, nm_act_request_get_connection (req), error)) {
+		g_assert (*error);
+		return FALSE;
+	}
+
+	priv->act_request = g_object_ref (req);
+	priv->secrets_updated_id = g_signal_connect (req,
+										"connection-secrets-updated",
+										G_CALLBACK (connection_secrets_updated_cb),
+										device);
+	priv->secrets_failed_id = g_signal_connect (req,
+									    "connection-secrets-failed",
+									    G_CALLBACK (connection_secrets_failed_cb),
+									    device);
+
+	if (!nm_act_request_get_assumed (req)) {
+		/* HACK: update the state a bit early to avoid a race between the 
+		 * scheduled stage1 handler and nm_policy_device_change_check() thinking
+		 * that the activation request isn't deferred because the deferred bit
+		 * gets cleared a bit too early, when the connection becomes valid.
+		 */
+		nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
+		nm_device_activate_schedule_stage1_device_prepare (self);
+	} else {
+		/* If it's an assumed connection, let the device subclass short-circuit
+		 * the normal connection process and just copy its IP configs from the
+		 * interface.
+		 */
+		nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
+		nm_device_activate_schedule_stage3_ip_config_start (self);
+	}
+
+	return TRUE;
+}
+
+/*
+ * nm_device_is_activating
+ *
+ * Return whether or not the device is currently activating itself.
+ *
+ */
+gboolean
+nm_device_is_activating (NMDevice *device)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+	switch (nm_device_get_state (device)) {
+	case NM_DEVICE_STATE_PREPARE:
+	case NM_DEVICE_STATE_CONFIG:
+	case NM_DEVICE_STATE_NEED_AUTH:
+	case NM_DEVICE_STATE_IP_CONFIG:
+		return TRUE;
+		break;
+	default:
+		break;
+	}
+
+	/* There's a small race between the time when stage 1 is scheduled
+	 * and when the device actually sets STATE_PREPARE when the activation
+	 * handler is actually run.  If there's an activation handler scheduled
+	 * we're activating anyway.
+	 */
+	if (priv->act_source_id)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+gboolean
+nm_device_can_interrupt_activation (NMDevice *self)
+{
+	gboolean	interrupt = FALSE;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+
+	if (NM_DEVICE_GET_CLASS (self)->can_interrupt_activation)
+		interrupt = NM_DEVICE_GET_CLASS (self)->can_interrupt_activation (self);
+	return interrupt;
+}
+
+/* IP Configuration stuff */
+
+NMDHCP4Config *
+nm_device_get_dhcp4_config (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->dhcp4_config;
+}
+
+NMIP4Config *
+nm_device_get_ip4_config (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->ip4_config;
+}
+
+
+static gboolean
+nm_device_set_ip4_config (NMDevice *self,
+                          NMIP4Config *new_config,
+                          gboolean assumed,
+                          NMDeviceStateReason *reason)
+{
+	NMDevicePrivate *priv;
+	const char *ip_iface;
+	NMIP4Config *old_config = NULL;
+	gboolean success = TRUE;
+	NMIP4ConfigCompareFlags diff = NM_IP4_COMPARE_FLAG_ALL;
+	NMNamedManager *named_mgr;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (reason != NULL, FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	ip_iface = nm_device_get_ip_iface (self);
+
+	old_config = priv->ip4_config;
+
+	if (new_config && old_config)
+		diff = nm_ip4_config_diff (new_config, old_config);
+
+	/* No actual change, do nothing */
+	if (diff == NM_IP4_COMPARE_FLAG_NONE)
+		return TRUE;
+
+	named_mgr = nm_named_manager_get ();
+	if (old_config) {
+		/* Remove any previous IP4 Config from the named manager */
+		nm_named_manager_remove_ip4_config (named_mgr, ip_iface, old_config);
+		g_object_unref (old_config);
+		priv->ip4_config = NULL;
+	}
+
+	if (new_config) {
+		priv->ip4_config = g_object_ref (new_config);
+
+		/* Don't touch the device's actual IP config if the connection is
+		 * assumed when NM starts.
+		 */
+		if (!assumed)
+			success = nm_system_apply_ip4_config (ip_iface, new_config, nm_device_get_priority (self), diff);
+
+		if (success || assumed) {
+			/* Export over D-Bus */
+			if (!nm_ip4_config_get_dbus_path (new_config))
+				nm_ip4_config_export (new_config);
+
+			/* Add the DNS information to the named manager */
+			nm_named_manager_add_ip4_config (named_mgr, ip_iface, new_config, NM_NAMED_IP_CONFIG_TYPE_DEFAULT);
+
+			nm_device_update_ip4_address (self);
+		}
+	}
+	g_object_unref (named_mgr);
+
+	g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP4_CONFIG);
+
+	return success;
+}
+
+/*
+ * nm_device_get_ip4_address
+ *
+ * Get a device's IPv4 address
+ *
+ */
+guint32
+nm_device_get_ip4_address (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, 0);
+
+	return NM_DEVICE_GET_PRIVATE (self)->ip4_address;
+}
+
+
+void
+nm_device_update_ip4_address (NMDevice *self)
+{
+	struct ifreq req;
+	guint32 new_address;
+	int fd;
+	
+	g_return_if_fail (self  != NULL);
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_IP4, "couldn't open control socket.");
+		return;
+	}
+
+	memset (&req, 0, sizeof (struct ifreq));
+	strncpy (req.ifr_name, nm_device_get_ip_iface (self), IFNAMSIZ);
+	if (ioctl (fd, SIOCGIFADDR, &req) == 0) {
+		new_address = ((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr;
+		if (new_address != nm_device_get_ip4_address (self))
+			NM_DEVICE_GET_PRIVATE (self)->ip4_address = new_address;
+	}
+	close (fd);
+}
+
+static gboolean
+nm_device_set_ip6_config (NMDevice *self,
+                          NMIP6Config *new_config,
+                          gboolean assumed,
+                          NMDeviceStateReason *reason)
+{
+	NMDevicePrivate *priv;
+	const char *ip_iface;
+	NMIP6Config *old_config = NULL;
+	gboolean success = TRUE;
+	NMIP6ConfigCompareFlags diff = NM_IP6_COMPARE_FLAG_ALL;
+	NMNamedManager *named_mgr;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (reason != NULL, FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	ip_iface = nm_device_get_ip_iface (self);
+
+	old_config = priv->ip6_config;
+
+	if (new_config && old_config)
+		diff = nm_ip6_config_diff (new_config, old_config);
+
+	/* No actual change, do nothing */
+	if (diff == NM_IP6_COMPARE_FLAG_NONE)
+		return TRUE;
+
+	named_mgr = nm_named_manager_get ();
+	if (old_config) {
+		/* Remove any previous IP6 Config from the named manager */
+		nm_named_manager_remove_ip6_config (named_mgr, ip_iface, old_config);
+		g_object_unref (old_config);
+		priv->ip6_config = NULL;
+	}
+
+	if (new_config) {
+		priv->ip6_config = g_object_ref (new_config);
+
+		/* Don't touch the device's actual IP config if the connection is
+		 * assumed when NM starts.
+		 */
+		if (!assumed)
+			success = nm_system_apply_ip6_config (ip_iface, new_config, nm_device_get_priority (self), diff);
+
+		if (success || assumed) {
+			/* Export over D-Bus */
+			if (!nm_ip6_config_get_dbus_path (new_config))
+				nm_ip6_config_export (new_config);
+
+			/* Add the DNS information to the named manager */
+			nm_named_manager_add_ip6_config (named_mgr, ip_iface, new_config, NM_NAMED_IP_CONFIG_TYPE_DEFAULT);
+		}
+	}
+	g_object_unref (named_mgr);
+
+	g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP6_CONFIG);
+
+	return success;
+}
+
+NMDHCP6Config *
+nm_device_get_dhcp6_config (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->dhcp6_config;
+}
+
+NMIP6Config *
+nm_device_get_ip6_config (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->ip6_config;
+}
+
+static gboolean
+nm_device_is_up (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	if (NM_DEVICE_GET_CLASS (self)->is_up)
+		return NM_DEVICE_GET_CLASS (self)->is_up (self);
+
+	return TRUE;
+}
+
+gboolean
+nm_device_hw_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
+{
+	gboolean success;
+	guint32 tries = 0;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	if (nm_device_hw_is_up (self))
+		goto out;
+
+	nm_log_info (LOGD_HW, "(%s): bringing up device.", nm_device_get_iface (self));
+
+	if (NM_DEVICE_GET_CLASS (self)->hw_bring_up) {
+		success = NM_DEVICE_GET_CLASS (self)->hw_bring_up (self, no_firmware);
+		if (!success)
+			return FALSE;
+	}
+
+	/* Wait for the device to come up if requested */
+	while (block && !nm_device_hw_is_up (self) && (tries++ < 50))
+		g_usleep (200);
+
+	if (!nm_device_hw_is_up (self)) {
+		nm_log_warn (LOGD_HW, "(%s): device not up after timeout!", nm_device_get_iface (self));
+		return FALSE;
+	}
+
+out:
+	/* Can only get HW address of some devices when they are up */
+	if (NM_DEVICE_GET_CLASS (self)->update_hw_address)
+		NM_DEVICE_GET_CLASS (self)->update_hw_address (self);
+
+	nm_device_update_ip4_address (self);
+	return TRUE;
+}
+
+void
+nm_device_hw_take_down (NMDevice *self, gboolean block)
+{
+	guint32 tries = 0;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	if (!nm_device_hw_is_up (self))
+		return;
+
+	nm_log_info (LOGD_HW, "(%s): taking down device.", nm_device_get_iface (self));
+
+	if (NM_DEVICE_GET_CLASS (self)->hw_take_down)
+		NM_DEVICE_GET_CLASS (self)->hw_take_down (self);
+
+	/* Wait for the device to come up if requested */
+	while (block && nm_device_hw_is_up (self) && (tries++ < 50))
+		g_usleep (200);
+}
+
+static gboolean
+nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
+{
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	if (!nm_device_hw_bring_up (self, block, no_firmware))
+		return FALSE;
+
+	if (nm_device_is_up (self))
+		return TRUE;
+
+	nm_log_info (LOGD_HW, "(%s): preparing device.", nm_device_get_iface (self));
+
+	if (NM_DEVICE_GET_CLASS (self)->bring_up)
+		success = NM_DEVICE_GET_CLASS (self)->bring_up (self);
+
+	return success;
+}
+
+static void
+nm_device_take_down (NMDevice *self, gboolean block, NMDeviceStateReason reason)
+{
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	if (nm_device_get_act_request (self))
+		nm_device_interface_deactivate (NM_DEVICE_INTERFACE (self), reason);
+
+	if (nm_device_is_up (self)) {
+		nm_log_info (LOGD_HW, "(%s): cleaning up...", nm_device_get_iface (self));
+
+		if (NM_DEVICE_GET_CLASS (self)->take_down)
+			NM_DEVICE_GET_CLASS (self)->take_down (self);
+	}
+
+	nm_device_hw_take_down (self, block);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDevice *self = NM_DEVICE (object);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gboolean take_down = TRUE;
+
+	if (priv->disposed || !priv->initialized)
+		goto out;
+
+	priv->disposed = TRUE;
+
+	/* Don't down can-assume-connection capable devices that are activated with
+	 * a connection that can be assumed.
+	 */
+	if (   nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (self))
+	    && (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) {
+		NMConnection *connection;
+	    NMSettingIP4Config *s_ip4;
+		const char *method = NULL;
+
+		/* Only system connections can be left up */
+		connection = nm_act_request_get_connection (priv->act_request);
+		if (   connection
+		    && (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_SYSTEM)) {
+
+			/* Only static or DHCP IPv4 connections can be left up.
+			 * All IPv6 connections can be left up, so we don't have
+			 * to check that.
+			 */
+			s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+			g_assert (s_ip4);
+
+			method = nm_setting_ip4_config_get_method (s_ip4);
+			if (   !method
+			    || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)
+			    || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
+				take_down = FALSE;
+		}
+	}
+
+	/* Clear any delayed transitions */
+	delayed_transitions_clear (self);
+
+	/* Clean up and stop DHCP */
+	dhcp4_cleanup (self, take_down);
+	dhcp6_cleanup (self, take_down);
+	addrconf6_cleanup (self);
+	dnsmasq_cleanup (self);
+
+	/* Take the device itself down and clear its IPv4 configuration */
+	if (priv->managed && take_down) {
+		NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
+
+		nm_device_take_down (self, FALSE, NM_DEVICE_STATE_REASON_REMOVED);
+		nm_device_set_ip4_config (self, NULL, FALSE, &ignored);
+	}
+
+	/* reset the saved RA value */
+	if (priv->ip6_accept_ra_path) {
+		nm_utils_do_sysctl (priv->ip6_accept_ra_path,
+		                    priv->ip6_accept_ra_save ? "1\n" : "0\n");
+	}
+	g_free (priv->ip6_accept_ra_path);
+
+	activation_source_clear (self, TRUE, AF_INET);
+	activation_source_clear (self, TRUE, AF_INET6);
+
+	clear_act_request (self);
+
+out:
+	G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDevice *self = NM_DEVICE (object);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	g_object_unref (priv->dhcp_manager);
+
+	g_free (priv->udi);
+	g_free (priv->iface);
+	g_free (priv->ip_iface);
+	g_free (priv->driver);
+	g_free (priv->type_desc);
+	if (priv->dhcp_anycast_address)
+		g_byte_array_free (priv->dhcp_anycast_address, TRUE);
+
+	G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
+}
+
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
+ 
+	switch (prop_id) {
+	case NM_DEVICE_INTERFACE_PROP_UDI:
+		/* construct-only */
+		priv->udi = g_strdup (g_value_get_string (value));
+		break;
+	case NM_DEVICE_INTERFACE_PROP_IFACE:
+		g_free (priv->iface);
+		priv->ifindex = 0;
+		priv->iface = g_value_dup_string (value);
+		if (priv->iface) {
+			priv->ifindex = nm_netlink_iface_to_index (priv->iface);
+			if (priv->ifindex < 0) {
+				nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface);
+			}
+		}
+		break;
+	case NM_DEVICE_INTERFACE_PROP_DRIVER:
+		priv->driver = g_strdup (g_value_get_string (value));
+		break;
+	case NM_DEVICE_INTERFACE_PROP_CAPABILITIES:
+		priv->capabilities = g_value_get_uint (value);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS:
+		priv->ip4_address = g_value_get_uint (value);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_MANAGED:
+		priv->managed = g_value_get_boolean (value);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE:
+		g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN);
+		priv->type = g_value_get_uint (value);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_TYPE_DESC:
+		g_free (priv->type_desc);
+		priv->type_desc = g_value_dup_string (value);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE:
+		priv->rfkill_type = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMDevice *self = NM_DEVICE (object);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceState state;
+
+	state = nm_device_get_state (self);
+
+	switch (prop_id) {
+	case NM_DEVICE_INTERFACE_PROP_UDI:
+		g_value_set_string (value, priv->udi);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_IFACE:
+		g_value_set_string (value, priv->iface);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_IFINDEX:
+		g_value_set_int (value, priv->ifindex);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_DRIVER:
+		g_value_set_string (value, priv->driver);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_CAPABILITIES:
+		g_value_set_uint (value, priv->capabilities);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS:
+		g_value_set_uint (value, priv->ip4_address);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_IP4_CONFIG:
+		if ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG)) {
+			if (priv->ip4_config) {
+				g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config));
+				break;
+			}
+		}
+		g_value_set_boxed (value, "/");
+		break;
+	case NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG:
+		if (   ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG))
+		    && priv->dhcp4_client)
+			g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config));
+		else
+			g_value_set_boxed (value, "/");
+		break;
+	case NM_DEVICE_INTERFACE_PROP_IP6_CONFIG:
+		if ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG)) {
+			if (priv->ip6_config) {
+				g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config));
+				break;
+			}
+		}
+		g_value_set_boxed (value, "/");
+		break;
+	case NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG:
+		if (   ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG))
+		    && priv->dhcp6_client)
+			g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config));
+		else
+			g_value_set_boxed (value, "/");
+		break;
+	case NM_DEVICE_INTERFACE_PROP_STATE:
+		g_value_set_uint (value, priv->state);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE:
+		g_value_set_uint (value, priv->type);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_MANAGED:
+		g_value_set_boolean (value, priv->managed);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_TYPE_DESC:
+		g_value_set_string (value, priv->type_desc);
+		break;
+	case NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE:
+		g_value_set_uint (value, priv->rfkill_type);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+
+static void
+nm_device_class_init (NMDeviceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMDevicePrivate));
+
+	/* Virtual methods */
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->constructor = constructor;
+
+	klass->get_type_capabilities = real_get_type_capabilities;
+	klass->get_generic_capabilities = real_get_generic_capabilities;
+	klass->act_stage1_prepare = real_act_stage1_prepare;
+	klass->act_stage2_config = real_act_stage2_config;
+	klass->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start;
+	klass->act_stage3_ip6_config_start = real_act_stage3_ip6_config_start;
+	klass->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
+	klass->act_stage4_get_ip6_config = real_act_stage4_get_ip6_config;
+	klass->act_stage4_ip4_config_timeout = real_act_stage4_ip4_config_timeout;
+	klass->act_stage4_ip6_config_timeout = real_act_stage4_ip6_config_timeout;
+
+	/* Properties */
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_UDI,
+									  NM_DEVICE_INTERFACE_UDI);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_IFACE,
+									  NM_DEVICE_INTERFACE_IFACE);
+
+	g_object_class_override_property (object_class,
+	                                  NM_DEVICE_INTERFACE_PROP_IFINDEX,
+	                                  NM_DEVICE_INTERFACE_IFINDEX);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_DRIVER,
+									  NM_DEVICE_INTERFACE_DRIVER);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_CAPABILITIES,
+									  NM_DEVICE_INTERFACE_CAPABILITIES);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS,
+									  NM_DEVICE_INTERFACE_IP4_ADDRESS);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_IP4_CONFIG,
+									  NM_DEVICE_INTERFACE_IP4_CONFIG);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG,
+									  NM_DEVICE_INTERFACE_DHCP4_CONFIG);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_IP6_CONFIG,
+									  NM_DEVICE_INTERFACE_IP6_CONFIG);
+
+	g_object_class_override_property (object_class,
+	                                  NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG,
+	                                  NM_DEVICE_INTERFACE_DHCP6_CONFIG);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_STATE,
+									  NM_DEVICE_INTERFACE_STATE);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE,
+									  NM_DEVICE_INTERFACE_DEVICE_TYPE);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_MANAGED,
+									  NM_DEVICE_INTERFACE_MANAGED);
+
+	g_object_class_override_property (object_class,
+									  NM_DEVICE_INTERFACE_PROP_TYPE_DESC,
+									  NM_DEVICE_INTERFACE_TYPE_DESC);
+
+	g_object_class_override_property (object_class,
+	                                  NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE,
+	                                  NM_DEVICE_INTERFACE_RFKILL_TYPE);
+
+	signals[AUTOCONNECT_ALLOWED] =
+		g_signal_new ("autoconnect-allowed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              0,
+		              autoconnect_allowed_accumulator, NULL,
+		              _nm_marshal_BOOLEAN__VOID,
+		              G_TYPE_BOOLEAN, 0);
+}
+
+static gboolean
+failed_to_disconnected (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	nm_log_dbg (LOGD_DEVICE, "(%s): running failed->disconnected transition",
+	            nm_device_get_iface (self));
+	priv->failed_to_disconnected_id = 0;
+	nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
+	return FALSE;
+}
+
+static gboolean
+unavailable_to_disconnected (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	nm_log_dbg (LOGD_DEVICE, "(%s): running unavailable->disconnected transition",
+	            nm_device_get_iface (self));
+	priv->unavailable_to_disconnected_id = 0;
+	nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
+	return FALSE;
+}
+
+void
+nm_device_state_changed (NMDevice *device,
+                         NMDeviceState state,
+                         NMDeviceStateReason reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDeviceState old_state;
+	NMActRequest *req;
+	gboolean no_firmware = FALSE;
+
+	g_return_if_fail (NM_IS_DEVICE (device));
+
+	if (priv->state == state)
+		return;
+
+	old_state = priv->state;
+	priv->state = state;
+
+	nm_log_info (LOGD_DEVICE, "(%s): device state change: %d -> %d (reason %d)",
+	             nm_device_get_iface (device), old_state, state, reason);
+
+	/* Clear any delayed transitions */
+	delayed_transitions_clear (device);
+
+	/* Cache the activation request for the dispatcher */
+	req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
+
+	/* Handle the new state here; but anything that could trigger
+	 * another state change should be done below.
+	 */
+	switch (state) {
+	case NM_DEVICE_STATE_UNMANAGED:
+		if (old_state > NM_DEVICE_STATE_UNMANAGED)
+			nm_device_take_down (device, TRUE, reason);
+		break;
+	case NM_DEVICE_STATE_UNAVAILABLE:
+		if (old_state == NM_DEVICE_STATE_UNMANAGED) {
+			if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware) {
+				nm_log_warn (LOGD_HW, "%s: firmware may be missing.", nm_device_get_iface (device));
+			}
+		}
+		/* Ensure the device gets deactivated in response to stuff like
+		 * carrier changes or rfkill.  But don't deactivate devices that are
+		 * about to assume a connection since that defeats the purpose of
+		 * assuming the device's existing connection.
+		 */
+		if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED)
+			nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device), reason);
+		break;
+	case NM_DEVICE_STATE_DISCONNECTED:
+		if (old_state != NM_DEVICE_STATE_UNAVAILABLE)
+			nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device), reason);
+		break;
+	default:
+		priv->autoconnect_inhibit = FALSE;
+		break;
+	}
+
+	g_object_notify (G_OBJECT (device), NM_DEVICE_INTERFACE_STATE);
+	g_signal_emit_by_name (device, "state-changed", state, old_state, reason);
+
+	/* Post-process the event after internal notification */
+
+	switch (state) {
+	case NM_DEVICE_STATE_UNAVAILABLE:
+		/* If the device can activate now (ie, it's got a carrier, the supplicant
+		 * is active, or whatever) schedule a delayed transition to DISCONNECTED
+		 * to get things rolling.  The device can't transition immediately becuase
+		 * we can't change states again from the state handler for a variety of
+		 * reasons.
+		 */
+		if (nm_device_is_available (device)) {
+			nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED",
+			            nm_device_get_iface (device));
+			priv->unavailable_to_disconnected_id = g_idle_add (unavailable_to_disconnected, device);
+		} else {
+			nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED",
+			            nm_device_get_iface (device));
+		}
+		break;
+	case NM_DEVICE_STATE_ACTIVATED:
+		nm_log_info (LOGD_DEVICE, "Activation (%s) successful, device activated.",
+		             nm_device_get_iface (device));
+		nm_utils_call_dispatcher ("up", nm_act_request_get_connection (req), device, NULL);
+		break;
+	case NM_DEVICE_STATE_FAILED:
+		nm_log_warn (LOGD_DEVICE, "Activation (%s) failed.", nm_device_get_iface (device));
+		/* Schedule the transition to DISCONNECTED.  The device can't transition
+		 * immediately becuase we can't change states again from the state
+		 * handler for a variety of reasons.
+		 */
+		priv->failed_to_disconnected_id = g_idle_add (failed_to_disconnected, device);
+		break;
+	default:
+		break;
+	}
+
+	if (old_state == NM_DEVICE_STATE_ACTIVATED)
+		nm_utils_call_dispatcher ("down", nm_act_request_get_connection (req), device, NULL);
+
+	/* Dispose of the cached activation request */
+	if (req)
+		g_object_unref (req);
+}
+
+NMDeviceState
+nm_device_get_state (NMDevice *device)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
+
+	return NM_DEVICE_GET_PRIVATE (device)->state;
+}
+
+gboolean
+nm_device_get_managed (NMDevice *device)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+	return NM_DEVICE_GET_PRIVATE (device)->managed;
+}
+
+void
+nm_device_set_managed (NMDevice *device,
+                       gboolean managed,
+                       NMDeviceStateReason reason)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (device));
+
+	priv = NM_DEVICE_GET_PRIVATE (device);
+	if (priv->managed == managed)
+		return;
+
+	priv->managed = managed;
+	nm_log_info (LOGD_DEVICE, "(%s): now %s",
+	             nm_device_get_iface (device),
+	             managed ? "managed" : "unmanaged");
+
+	g_object_notify (G_OBJECT (device), NM_DEVICE_INTERFACE_MANAGED);
+
+	/* If now managed, jump to unavailable */
+	if (managed)
+		nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, reason);
+	else
+		nm_device_state_changed (device, NM_DEVICE_STATE_UNMANAGED, reason);
+}
+
+static gboolean
+spec_match_list (NMDeviceInterface *device, const GSList *specs)
+{
+	NMDevice *self;
+
+	g_return_val_if_fail (device != NULL, FALSE);
+
+	self = NM_DEVICE (device);
+	if (NM_DEVICE_GET_CLASS (self)->spec_match_list)
+		return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs);
+
+	return FALSE;
+}
+
+static NMConnection *
+connection_match_config (NMDeviceInterface *device, const GSList *connections)
+{
+	g_return_val_if_fail (device != NULL, FALSE);
+
+	if (NM_DEVICE_GET_CLASS (device)->connection_match_config)
+		return NM_DEVICE_GET_CLASS (device)->connection_match_config (NM_DEVICE (device), connections);
+	return NULL;
+}
+
+void
+nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout)
+{
+	g_return_if_fail (NM_IS_DEVICE (device));
+
+	NM_DEVICE_GET_PRIVATE (device)->dhcp_timeout = timeout;
+}
+
+void
+nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (device));
+
+	priv = NM_DEVICE_GET_PRIVATE (device);
+
+	if (priv->dhcp_anycast_address) {
+		g_byte_array_free (priv->dhcp_anycast_address, TRUE);
+		priv->dhcp_anycast_address = NULL;
+	}
+
+	if (addr) {
+		priv->dhcp_anycast_address = g_byte_array_sized_new (ETH_ALEN);
+		g_byte_array_append (priv->dhcp_anycast_address, addr, ETH_ALEN);
+	}
+}
+
+
+void
+nm_device_clear_autoconnect_inhibit (NMDevice *device)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	g_return_if_fail (priv);
+	priv->autoconnect_inhibit = FALSE;
+}
+
diff --git a/src/nm-device.h b/src/nm-device.h
new file mode 100644
index 00000000..202f3924
--- /dev/null
+++ b/src/nm-device.h
@@ -0,0 +1,188 @@
+/* -*- 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_DEVICE_H
+#define NM_DEVICE_H
+
+#include <glib-object.h>
+#include <dbus/dbus.h>
+#include <netinet/in.h>
+
+#include "NetworkManager.h"
+#include "nm-activation-request.h"
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
+#include "nm-dhcp4-config.h"
+#include "nm-dhcp6-config.h"
+#include "nm-connection.h"
+
+typedef enum NMActStageReturn
+{
+	NM_ACT_STAGE_RETURN_FAILURE = 0,
+	NM_ACT_STAGE_RETURN_SUCCESS,
+	NM_ACT_STAGE_RETURN_POSTPONE,
+	NM_ACT_STAGE_RETURN_STOP         /* This activation chain is done */
+} NMActStageReturn;
+
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE			(nm_device_get_type ())
+#define NM_DEVICE(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice))
+#define NM_DEVICE_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE, NMDeviceClass))
+#define NM_IS_DEVICE(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE))
+#define NM_IS_DEVICE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE))
+#define NM_DEVICE_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE, NMDeviceClass))
+
+typedef struct {
+	GObject parent;
+} NMDevice;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Hardware state, ie IFF_UP */
+	gboolean        (*hw_is_up)      (NMDevice *self);
+	gboolean        (*hw_bring_up)   (NMDevice *self, gboolean *no_firmware);
+	void            (*hw_take_down)  (NMDevice *self);
+
+	/* Additional stuff required to operate the device, like a 
+	 * connection to the supplicant, Bluez, etc
+	 */
+	gboolean        (*is_up)         (NMDevice *self);
+	gboolean        (*bring_up)      (NMDevice *self);
+	void            (*take_down)     (NMDevice *self);
+
+	void        (* update_hw_address) (NMDevice *self);
+
+	guint32		(* get_type_capabilities)	(NMDevice *self);
+	guint32		(* get_generic_capabilities)	(NMDevice *self);
+
+	gboolean	(* is_available) (NMDevice *self);
+
+	NMConnection * (* get_best_auto_connection) (NMDevice *self,
+	                                             GSList *connections,
+	                                             char **specific_object);
+
+	void        (* connection_secrets_updated) (NMDevice *self,
+	                                            NMConnection *connection,
+	                                            GSList *updated_settings,
+	                                            RequestSecretsCaller caller);
+
+	gboolean    (* check_connection_compatible) (NMDevice *self,
+	                                             NMConnection *connection,
+	                                             GError **error);
+
+	NMActStageReturn	(* act_stage1_prepare)	(NMDevice *self,
+	                                             NMDeviceStateReason *reason);
+	NMActStageReturn	(* act_stage2_config)	(NMDevice *self,
+	                                             NMDeviceStateReason *reason);
+	NMActStageReturn	(* act_stage3_ip4_config_start) (NMDevice *self,
+														 NMDeviceStateReason *reason);
+	NMActStageReturn	(* act_stage3_ip6_config_start) (NMDevice *self,
+														 NMDeviceStateReason *reason);
+	NMActStageReturn	(* act_stage4_get_ip4_config)	(NMDevice *self,
+														 NMIP4Config **config,
+	                                                     NMDeviceStateReason *reason);
+	NMActStageReturn	(* act_stage4_get_ip6_config)	(NMDevice *self,
+														 NMIP6Config **config,
+	                                                     NMDeviceStateReason *reason);
+	NMActStageReturn	(* act_stage4_ip4_config_timeout)	(NMDevice *self,
+	                                                         NMIP4Config **config,
+	                                                         NMDeviceStateReason *reason);
+	NMActStageReturn	(* act_stage4_ip6_config_timeout)	(NMDevice *self,
+	                                                         NMIP6Config **config,
+	                                                         NMDeviceStateReason *reason);
+	void			(* deactivate)			(NMDevice *self);
+	void			(* deactivate_quickly)	(NMDevice *self);
+
+	gboolean		(* can_interrupt_activation)		(NMDevice *self);
+
+	gboolean        (* spec_match_list)     (NMDevice *self, const GSList *specs);
+
+	NMConnection *  (* connection_match_config) (NMDevice *self, const GSList *connections);
+} NMDeviceClass;
+
+
+GType nm_device_get_type (void);
+
+const char *    nm_device_get_path (NMDevice *dev);
+void            nm_device_set_path (NMDevice *dev, const char *path);
+
+const char *	nm_device_get_udi		(NMDevice *dev);
+const char *	nm_device_get_iface		(NMDevice *dev);
+int             nm_device_get_ifindex	(NMDevice *dev);
+const char *	nm_device_get_ip_iface	(NMDevice *dev);
+int             nm_device_get_ip_ifindex(NMDevice *dev);
+const char *	nm_device_get_driver	(NMDevice *dev);
+const char *	nm_device_get_type_desc (NMDevice *dev);
+
+NMDeviceType	nm_device_get_device_type	(NMDevice *dev);
+guint32		nm_device_get_capabilities	(NMDevice *dev);
+guint32		nm_device_get_type_capabilities	(NMDevice *dev);
+
+int			nm_device_get_priority (NMDevice *dev);
+
+guint32			nm_device_get_ip4_address	(NMDevice *dev);
+void				nm_device_update_ip4_address	(NMDevice *dev);
+
+NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *dev);
+NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *dev);
+
+NMIP4Config *	nm_device_get_ip4_config	(NMDevice *dev);
+NMIP6Config *	nm_device_get_ip6_config	(NMDevice *dev);
+
+void *		nm_device_get_system_config_data	(NMDevice *dev);
+
+NMActRequest *	nm_device_get_act_request	(NMDevice *dev);
+
+gboolean		nm_device_is_available (NMDevice *dev);
+
+NMConnection * nm_device_get_best_auto_connection (NMDevice *dev,
+                                                   GSList *connections,
+                                                   char **specific_object);
+
+void			nm_device_activate_schedule_stage1_device_prepare		(NMDevice *device);
+void			nm_device_activate_schedule_stage2_device_config		(NMDevice *device);
+void			nm_device_activate_schedule_stage4_ip4_config_get		(NMDevice *device);
+void			nm_device_activate_schedule_stage4_ip4_config_timeout	(NMDevice *device);
+void			nm_device_activate_schedule_stage4_ip6_config_get		(NMDevice *device);
+void			nm_device_activate_schedule_stage4_ip6_config_timeout	(NMDevice *device);
+gboolean		nm_device_deactivate_quickly	(NMDevice *dev);
+gboolean		nm_device_is_activating		(NMDevice *dev);
+gboolean		nm_device_can_interrupt_activation		(NMDevice *self);
+gboolean		nm_device_autoconnect_allowed	(NMDevice *self);
+
+NMDeviceState nm_device_get_state (NMDevice *device);
+
+gboolean nm_device_get_managed (NMDevice *device);
+void nm_device_set_managed (NMDevice *device,
+                            gboolean managed,
+                            NMDeviceStateReason reason);
+
+void nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout);
+void nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr);
+
+void nm_device_clear_autoconnect_inhibit (NMDevice *device);
+
+G_END_DECLS
+
+#endif	/* NM_DEVICE_H */
diff --git a/src/nm-dhcp4-config.c b/src/nm-dhcp4-config.c
new file mode 100644
index 00000000..5acf053f
--- /dev/null
+++ b/src/nm-dhcp4-config.c
@@ -0,0 +1,192 @@
+/* -*- 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) 2008 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "NetworkManager.h"
+#include "nm-dbus-manager.h"
+#include "nm-dhcp4-config.h"
+#include "nm-dhcp4-config-glue.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-utils.h"
+
+
+G_DEFINE_TYPE (NMDHCP4Config, nm_dhcp4_config, G_TYPE_OBJECT)
+
+#define NM_DHCP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP4_CONFIG, NMDHCP4ConfigPrivate))
+
+typedef struct {
+	char *dbus_path;
+	GHashTable *options;
+} NMDHCP4ConfigPrivate;
+
+
+enum {
+	PROP_0,
+	PROP_OPTIONS,
+
+	LAST_PROP
+};
+
+enum {
+	PROPERTIES_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+NMDHCP4Config *
+nm_dhcp4_config_new (void)
+{
+	return NM_DHCP4_CONFIG (g_object_new (NM_TYPE_DHCP4_CONFIG, NULL));
+}
+
+void
+nm_dhcp4_config_add_option (NMDHCP4Config *self,
+                            const char *key,
+                            const char *option)
+{
+	GValue *svalue;
+
+	g_return_if_fail (NM_IS_DHCP4_CONFIG (self));
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (option != NULL);
+
+	svalue = g_slice_new0 (GValue);
+	g_value_init (svalue, G_TYPE_STRING);
+	g_value_set_string (svalue, option);
+	g_hash_table_insert (NM_DHCP4_CONFIG_GET_PRIVATE (self)->options, g_strdup (key), svalue);
+	g_object_notify (G_OBJECT (self), NM_DHCP4_CONFIG_OPTIONS);
+}
+
+void
+nm_dhcp4_config_reset (NMDHCP4Config *self)
+{
+	g_return_if_fail (NM_IS_DHCP4_CONFIG (self));
+
+	g_hash_table_remove_all (NM_DHCP4_CONFIG_GET_PRIVATE (self)->options);
+	g_object_notify (G_OBJECT (self), NM_DHCP4_CONFIG_OPTIONS);
+}
+
+const char *
+nm_dhcp4_config_get_option (NMDHCP4Config *self, const char *key)
+{
+	GValue *value;
+
+	g_return_val_if_fail (NM_IS_DHCP4_CONFIG (self), NULL);
+	g_return_val_if_fail (key != NULL, NULL);
+
+	value = g_hash_table_lookup (NM_DHCP4_CONFIG_GET_PRIVATE (self)->options, key);
+	return value ? g_value_get_string (value) : NULL;
+}
+
+const char *
+nm_dhcp4_config_get_dbus_path (NMDHCP4Config *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP4_CONFIG (self), NULL);
+
+	return NM_DHCP4_CONFIG_GET_PRIVATE (self)->dbus_path;
+}
+
+static void
+nm_gvalue_destroy (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+static void
+nm_dhcp4_config_init (NMDHCP4Config *self)
+{
+	NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (self);
+	static guint32 counter = 0;
+	DBusGConnection *connection;
+	NMDBusManager *dbus_mgr;
+
+	dbus_mgr = nm_dbus_manager_get ();
+	connection = nm_dbus_manager_get_connection (dbus_mgr);
+	priv->dbus_path = g_strdup_printf (NM_DBUS_PATH "/DHCP4Config/%d", counter++);
+	dbus_g_connection_register_g_object (connection, priv->dbus_path, G_OBJECT (self));
+	g_object_unref (dbus_mgr);
+
+	priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object);
+
+	g_free (priv->dbus_path);
+	g_hash_table_destroy (priv->options);
+
+	G_OBJECT_CLASS (nm_dhcp4_config_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_OPTIONS:
+		g_value_set_boxed (value, priv->options);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_dhcp4_config_class_init (NMDHCP4ConfigClass *config_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+	g_type_class_add_private (config_class, sizeof (NMDHCP4ConfigPrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_OPTIONS,
+		 g_param_spec_boxed (NM_DHCP4_CONFIG_OPTIONS,
+		                     "Options",
+		                     "DHCP configuration options returned by the server",
+		                     DBUS_TYPE_G_MAP_OF_VARIANT,
+		                     G_PARAM_READABLE));
+
+	/* Signals */
+	signals[PROPERTIES_CHANGED] = 
+		nm_properties_changed_signal_new (object_class,
+								    G_STRUCT_OFFSET (NMDHCP4ConfigClass, properties_changed));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class),
+									 &dbus_glib_nm_dhcp4_config_object_info);
+}
diff --git a/src/nm-dhcp4-config.h b/src/nm-dhcp4-config.h
new file mode 100644
index 00000000..ffaa8430
--- /dev/null
+++ b/src/nm-dhcp4-config.h
@@ -0,0 +1,61 @@
+/* -*- 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) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP4_CONFIG_H
+#define NM_DHCP4_CONFIG_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define NM_TYPE_DHCP4_CONFIG            (nm_dhcp4_config_get_type ())
+#define NM_DHCP4_CONFIG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP4_CONFIG, NMDHCP4Config))
+#define NM_DHCP4_CONFIG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP4_CONFIG, NMDHCP4ConfigClass))
+#define NM_IS_DHCP4_CONFIG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP4_CONFIG))
+#define NM_IS_DHCP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP4_CONFIG))
+#define NM_DHCP4_CONFIG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP4_CONFIG, NMDHCP4ConfigClass))
+
+typedef struct {
+	GObject parent;
+} NMDHCP4Config;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*properties_changed) (NMDHCP4Config *config, GHashTable *properties);
+} NMDHCP4ConfigClass;
+
+#define NM_DHCP4_CONFIG_OPTIONS "options"
+
+GType nm_dhcp4_config_get_type (void);
+
+NMDHCP4Config *nm_dhcp4_config_new (void);
+
+const char *nm_dhcp4_config_get_dbus_path (NMDHCP4Config *config);
+
+void nm_dhcp4_config_add_option (NMDHCP4Config *config,
+                                 const char *key,
+                                 const char *option);
+
+void nm_dhcp4_config_reset (NMDHCP4Config *config);
+
+const char *nm_dhcp4_config_get_option (NMDHCP4Config *config, const char *option);
+
+#endif /* NM_DHCP4_CONFIG_H */
diff --git a/src/nm-dhcp6-config.c b/src/nm-dhcp6-config.c
new file mode 100644
index 00000000..fb6ccce5
--- /dev/null
+++ b/src/nm-dhcp6-config.c
@@ -0,0 +1,192 @@
+/* -*- 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) 2008 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "NetworkManager.h"
+#include "nm-dbus-manager.h"
+#include "nm-dhcp6-config.h"
+#include "nm-dhcp6-config-glue.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-utils.h"
+
+
+G_DEFINE_TYPE (NMDHCP6Config, nm_dhcp6_config, G_TYPE_OBJECT)
+
+#define NM_DHCP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigPrivate))
+
+typedef struct {
+	char *dbus_path;
+	GHashTable *options;
+} NMDHCP6ConfigPrivate;
+
+
+enum {
+	PROP_0,
+	PROP_OPTIONS,
+
+	LAST_PROP
+};
+
+enum {
+	PROPERTIES_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+NMDHCP6Config *
+nm_dhcp6_config_new (void)
+{
+	return NM_DHCP6_CONFIG (g_object_new (NM_TYPE_DHCP6_CONFIG, NULL));
+}
+
+void
+nm_dhcp6_config_add_option (NMDHCP6Config *self,
+                            const char *key,
+                            const char *option)
+{
+	GValue *svalue;
+
+	g_return_if_fail (NM_IS_DHCP6_CONFIG (self));
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (option != NULL);
+
+	svalue = g_slice_new0 (GValue);
+	g_value_init (svalue, G_TYPE_STRING);
+	g_value_set_string (svalue, option);
+	g_hash_table_insert (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options, g_strdup (key), svalue);
+	g_object_notify (G_OBJECT (self), NM_DHCP6_CONFIG_OPTIONS);
+}
+
+void
+nm_dhcp6_config_reset (NMDHCP6Config *self)
+{
+	g_return_if_fail (NM_IS_DHCP6_CONFIG (self));
+
+	g_hash_table_remove_all (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options);
+	g_object_notify (G_OBJECT (self), NM_DHCP6_CONFIG_OPTIONS);
+}
+
+const char *
+nm_dhcp6_config_get_option (NMDHCP6Config *self, const char *key)
+{
+	GValue *value;
+
+	g_return_val_if_fail (NM_IS_DHCP6_CONFIG (self), NULL);
+	g_return_val_if_fail (key != NULL, NULL);
+
+	value = g_hash_table_lookup (NM_DHCP6_CONFIG_GET_PRIVATE (self)->options, key);
+	return value ? g_value_get_string (value) : NULL;
+}
+
+const char *
+nm_dhcp6_config_get_dbus_path (NMDHCP6Config *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP6_CONFIG (self), NULL);
+
+	return NM_DHCP6_CONFIG_GET_PRIVATE (self)->dbus_path;
+}
+
+static void
+nm_gvalue_destroy (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+static void
+nm_dhcp6_config_init (NMDHCP6Config *self)
+{
+	NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (self);
+	static guint32 counter = 0;
+	DBusGConnection *connection;
+	NMDBusManager *dbus_mgr;
+
+	dbus_mgr = nm_dbus_manager_get ();
+	connection = nm_dbus_manager_get_connection (dbus_mgr);
+	priv->dbus_path = g_strdup_printf (NM_DBUS_PATH "/DHCP6Config/%d", counter++);
+	dbus_g_connection_register_g_object (connection, priv->dbus_path, G_OBJECT (self));
+	g_object_unref (dbus_mgr);
+
+	priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+
+	g_free (priv->dbus_path);
+	g_hash_table_destroy (priv->options);
+
+	G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_OPTIONS:
+		g_value_set_boxed (value, priv->options);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_dhcp6_config_class_init (NMDHCP6ConfigClass *config_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+	g_type_class_add_private (config_class, sizeof (NMDHCP6ConfigPrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_OPTIONS,
+		 g_param_spec_boxed (NM_DHCP6_CONFIG_OPTIONS,
+		                     "Options",
+		                     "DHCP configuration options returned by the server",
+		                     DBUS_TYPE_G_MAP_OF_VARIANT,
+		                     G_PARAM_READABLE));
+
+	/* Signals */
+	signals[PROPERTIES_CHANGED] = 
+		nm_properties_changed_signal_new (object_class,
+								    G_STRUCT_OFFSET (NMDHCP6ConfigClass, properties_changed));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class),
+									 &dbus_glib_nm_dhcp6_config_object_info);
+}
diff --git a/src/nm-dhcp6-config.h b/src/nm-dhcp6-config.h
new file mode 100644
index 00000000..90eb10ff
--- /dev/null
+++ b/src/nm-dhcp6-config.h
@@ -0,0 +1,61 @@
+/* -*- 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) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP6_CONFIG_H
+#define NM_DHCP6_CONFIG_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define NM_TYPE_DHCP6_CONFIG            (nm_dhcp6_config_get_type ())
+#define NM_DHCP6_CONFIG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6Config))
+#define NM_DHCP6_CONFIG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass))
+#define NM_IS_DHCP6_CONFIG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP6_CONFIG))
+#define NM_IS_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP6_CONFIG))
+#define NM_DHCP6_CONFIG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass))
+
+typedef struct {
+	GObject parent;
+} NMDHCP6Config;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*properties_changed) (NMDHCP6Config *config, GHashTable *properties);
+} NMDHCP6ConfigClass;
+
+#define NM_DHCP6_CONFIG_OPTIONS "options"
+
+GType nm_dhcp6_config_get_type (void);
+
+NMDHCP6Config *nm_dhcp6_config_new (void);
+
+const char *nm_dhcp6_config_get_dbus_path (NMDHCP6Config *config);
+
+void nm_dhcp6_config_add_option (NMDHCP6Config *config,
+                                 const char *key,
+                                 const char *option);
+
+void nm_dhcp6_config_reset (NMDHCP6Config *config);
+
+const char *nm_dhcp6_config_get_option (NMDHCP6Config *config, const char *option);
+
+#endif /* NM_DHCP6_CONFIG_H */
diff --git a/src/nm-hostname-provider.c b/src/nm-hostname-provider.c
new file mode 100644
index 00000000..1cd1ca6c
--- /dev/null
+++ b/src/nm-hostname-provider.c
@@ -0,0 +1,54 @@
+/* -*- 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) 2009 Novell, Inc.
+ */
+
+#include "nm-hostname-provider.h"
+
+const char *
+nm_hostname_provider_get_hostname (NMHostnameProvider *self)
+{
+	g_return_val_if_fail (NM_IS_HOSTNAME_PROVIDER (self), NULL);
+
+	return NM_HOSTNAME_PROVIDER_GET_INTERFACE (self)->get_hostname (self);
+}
+
+GType
+nm_hostname_provider_get_type (void)
+{
+    static GType type = 0;
+
+    if (!G_UNLIKELY (type)) {
+        const GTypeInfo type_info = {
+            sizeof (NMHostnameProvider), /* class_size */
+            NULL,   /* base_init */
+            NULL,       /* base_finalize */
+            NULL,
+            NULL,       /* class_finalize */
+            NULL,       /* class_data */
+            0,
+            0,              /* n_preallocs */
+            NULL
+        };
+
+        type = g_type_register_static (G_TYPE_INTERFACE, "NMHostnameProvider", &type_info, 0);
+        g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
+    }
+
+    return type;
+}
diff --git a/src/nm-hostname-provider.h b/src/nm-hostname-provider.h
new file mode 100644
index 00000000..7fe5c006
--- /dev/null
+++ b/src/nm-hostname-provider.h
@@ -0,0 +1,44 @@
+/* -*- 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) 2009 Novell, Inc.
+ */
+
+#ifndef NM_HOSTNAME_PROVIDER_H
+#define NM_HOSTNAME_PROVIDER_H
+
+#include <glib-object.h>
+
+#define NM_TYPE_HOSTNAME_PROVIDER (nm_hostname_provider_get_type ())
+#define NM_HOSTNAME_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_HOSTNAME_PROVIDER, NMHostnameProvider))
+#define NM_IS_HOSTNAME_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_HOSTNAME_PROVIDER))
+#define NM_HOSTNAME_PROVIDER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_HOSTNAME_PROVIDER, NMHostnameProvider))
+
+typedef struct _NMHostnameProvider NMHostnameProvider;
+
+struct _NMHostnameProvider {
+	GTypeInterface g_iface;
+
+	/* Methods */
+	const char *(*get_hostname) (NMHostnameProvider *self);
+};
+
+GType nm_hostname_provider_get_type (void);
+
+const char *nm_hostname_provider_get_hostname (NMHostnameProvider *self);
+
+#endif /* NM_HOSTNAME_PROVIDER_H */
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
new file mode 100644
index 00000000..0ae3d1be
--- /dev/null
+++ b/src/nm-ip4-config.c
@@ -0,0 +1,909 @@
+/* -*- 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include "nm-ip4-config.h"
+#include "nm-dbus-manager.h"
+#include "NetworkManager.h"
+#include "NetworkManagerUtils.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-utils.h"
+
+#include <netlink/route/addr.h>
+#include <netlink/utils.h>
+#include <netinet/in.h>
+
+#include "nm-ip4-config-glue.h"
+#include "nm-dbus-glib-types.h"
+
+
+G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, G_TYPE_OBJECT)
+
+#define NM_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP4_CONFIG, NMIP4ConfigPrivate))
+
+typedef struct {
+	char *path;
+
+	GSList *addresses;
+	guint32	ptp_address;
+
+	guint32	mtu;	/* Maximum Transmission Unit of the interface */
+	guint32	mss;	/* Maximum Segment Size of the route */
+
+	GArray *nameservers;
+	GPtrArray *domains;
+	GPtrArray *searches;
+
+	GArray *wins;
+
+	GArray *nis;
+	char * nis_domain;
+
+	GSList *routes;
+
+	gboolean never_default;
+} NMIP4ConfigPrivate;
+
+
+enum {
+	PROP_0,
+	PROP_ADDRESSES,
+	PROP_NAMESERVERS,
+	PROP_DOMAINS,
+	PROP_ROUTES,
+	PROP_WINS_SERVERS,
+
+	LAST_PROP
+};
+
+
+static struct nl_addr *
+nm_utils_ip4_addr_to_nl_addr (guint32 ip4_addr)
+{
+	struct nl_addr * nla = NULL;
+
+	if (!(nla = nl_addr_alloc (sizeof (in_addr_t))))
+		return NULL;
+	nl_addr_set_family (nla, AF_INET);
+	nl_addr_set_binary_addr (nla, &ip4_addr, sizeof (guint32));
+
+	return nla;
+}
+
+
+NMIP4Config *
+nm_ip4_config_new (void)
+{
+	return (NMIP4Config *) g_object_new (NM_TYPE_IP4_CONFIG, NULL);
+}
+
+void
+nm_ip4_config_export (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv;
+	NMDBusManager *dbus_mgr;
+	DBusGConnection *connection;
+	static guint32 counter = 0;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	g_return_if_fail (priv->path == NULL);
+
+	dbus_mgr = nm_dbus_manager_get ();
+	connection = nm_dbus_manager_get_connection (dbus_mgr);
+	priv->path = g_strdup_printf (NM_DBUS_PATH "/IP4Config/%d", counter++);
+
+	dbus_g_connection_register_g_object (connection, priv->path, G_OBJECT (config));
+	g_object_unref (dbus_mgr);
+}
+
+const char *
+nm_ip4_config_get_dbus_path (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), FALSE);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->path;
+}
+
+void
+nm_ip4_config_take_address (NMIP4Config *config, NMIP4Address *address)
+{
+	NMIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+	g_return_if_fail (address != NULL);
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	priv->addresses = g_slist_append (priv->addresses, address);
+}
+
+void
+nm_ip4_config_add_address (NMIP4Config *config,
+                           NMIP4Address *address)
+{
+	NMIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+	g_return_if_fail (address != NULL);
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	priv->addresses = g_slist_append (priv->addresses, nm_ip4_address_dup (address));
+}
+
+void
+nm_ip4_config_replace_address (NMIP4Config *config,
+                               guint i,
+                               NMIP4Address *new_address)
+{
+	NMIP4ConfigPrivate *priv;
+	GSList *old;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	old = g_slist_nth (priv->addresses, i);
+	g_return_if_fail (old != NULL);
+	nm_ip4_address_unref ((NMIP4Address *) old->data);
+
+	old->data = nm_ip4_address_dup (new_address);
+}
+
+NMIP4Address *nm_ip4_config_get_address (NMIP4Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+	return (NMIP4Address *) g_slist_nth_data (NM_IP4_CONFIG_GET_PRIVATE (config)->addresses, i);
+}
+
+guint32 nm_ip4_config_get_num_addresses (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return g_slist_length (NM_IP4_CONFIG_GET_PRIVATE (config)->addresses);
+}
+
+guint32 nm_ip4_config_get_ptp_address (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->ptp_address;
+}
+
+void nm_ip4_config_set_ptp_address (NMIP4Config *config, guint32 ptp_addr)
+{
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	NM_IP4_CONFIG_GET_PRIVATE (config)->ptp_address = ptp_addr;
+}
+
+void nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 nameserver)
+{
+	NMIP4ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+	g_return_if_fail (nameserver > 0);
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	for (i = 0; i < priv->nameservers->len; i++) {
+		guint32 s = g_array_index (priv->nameservers, guint32, i);
+
+		/* No dupes */
+		g_return_if_fail (nameserver != s);
+	}
+
+	g_array_append_val (priv->nameservers, nameserver);
+}
+
+guint32 nm_ip4_config_get_nameserver (NMIP4Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return g_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->nameservers, guint32, i);
+}
+
+guint32 nm_ip4_config_get_num_nameservers (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->nameservers->len;
+}
+
+void nm_ip4_config_reset_nameservers (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	if (priv->nameservers->len)
+		g_array_remove_range (priv->nameservers, 0, priv->nameservers->len);
+}
+
+void nm_ip4_config_add_wins (NMIP4Config *config, guint32 wins)
+{
+	NMIP4ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+	g_return_if_fail (wins > 0);
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	for (i = 0; i < priv->wins->len; i++) {
+		guint32 s = g_array_index (priv->wins, guint32, i);
+
+		/* No dupes */
+		g_return_if_fail (wins != s);
+	}
+
+	g_array_append_val (priv->wins, wins);
+}
+
+guint32 nm_ip4_config_get_wins (NMIP4Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return g_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->wins, guint32, i);
+}
+
+guint32 nm_ip4_config_get_num_wins (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->wins->len;
+}
+
+void nm_ip4_config_reset_wins (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	if (priv->wins->len)
+		g_array_remove_range (priv->wins, 0, priv->wins->len);
+}
+
+void
+nm_ip4_config_take_route (NMIP4Config *config, NMIP4Route *route)
+{
+	NMIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+	g_return_if_fail (route != NULL);
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	priv->routes = g_slist_append (priv->routes, route);
+}
+
+void
+nm_ip4_config_add_route (NMIP4Config *config, NMIP4Route *route)
+{
+	NMIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+	g_return_if_fail (route != NULL);
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	priv->routes = g_slist_append (priv->routes, nm_ip4_route_dup (route));
+}
+
+void
+nm_ip4_config_replace_route (NMIP4Config *config,
+							 guint i,
+							 NMIP4Route *new_route)
+{
+	NMIP4ConfigPrivate *priv;
+	GSList *old;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	old = g_slist_nth (priv->routes, i);
+	g_return_if_fail (old != NULL);
+	nm_ip4_route_unref ((NMIP4Route *) old->data);
+
+	old->data = nm_ip4_route_dup (new_route);
+}
+
+NMIP4Route *
+nm_ip4_config_get_route (NMIP4Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+	return (NMIP4Route *) g_slist_nth_data (NM_IP4_CONFIG_GET_PRIVATE (config)->routes, i);
+}
+
+guint32 nm_ip4_config_get_num_routes (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return g_slist_length (NM_IP4_CONFIG_GET_PRIVATE (config)->routes);
+}
+
+void nm_ip4_config_reset_routes (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	g_slist_foreach (priv->routes, (GFunc) g_free, NULL);
+	priv->routes = NULL;
+}
+
+void nm_ip4_config_add_domain (NMIP4Config *config, const char *domain)
+{
+	NMIP4ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+	g_return_if_fail (domain != NULL);
+	g_return_if_fail (strlen (domain) > 0);
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	for (i = 0; i < priv->domains->len; i++) {
+		if (!strcmp (g_ptr_array_index (priv->domains, i), domain))
+			return;
+	}
+
+	g_ptr_array_add (priv->domains, g_strdup (domain));
+}
+
+const char *nm_ip4_config_get_domain (NMIP4Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+	return (const char *) g_ptr_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->domains, i);
+}
+
+guint32 nm_ip4_config_get_num_domains (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->domains->len;
+}
+
+void nm_ip4_config_reset_domains (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	for (i = 0; i < priv->domains->len; i++)
+		g_free (g_ptr_array_index (priv->domains, i));
+	g_ptr_array_free (priv->domains, TRUE);
+	priv->domains = g_ptr_array_sized_new (3);
+}
+
+void nm_ip4_config_add_search (NMIP4Config *config, const char *search)
+{
+	NMIP4ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (config != NULL);
+	g_return_if_fail (search != NULL);
+	g_return_if_fail (strlen (search) > 0);
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	for (i = 0; i < priv->searches->len; i++) {
+		if (!strcmp (g_ptr_array_index (priv->searches, i), search))
+			return;
+	}
+
+	g_ptr_array_add (priv->searches, g_strdup (search));
+}
+
+const char *nm_ip4_config_get_search (NMIP4Config *config, guint i)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return (const char *) g_ptr_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->searches, i);
+}
+
+guint32 nm_ip4_config_get_num_searches (NMIP4Config *config)
+{
+	g_return_val_if_fail (config != NULL, 0);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->searches->len;
+}
+
+void nm_ip4_config_reset_searches (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	for (i = 0; i < priv->searches->len; i++)
+		g_free (g_ptr_array_index (priv->searches, i));
+	g_ptr_array_free (priv->searches, TRUE);
+	priv->searches = g_ptr_array_sized_new (3);
+}
+
+guint32 nm_ip4_config_get_mtu (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->mtu;
+}
+
+void nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu)
+{
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	NM_IP4_CONFIG_GET_PRIVATE (config)->mtu = mtu;
+}
+
+guint32 nm_ip4_config_get_mss (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->mss;
+}
+
+void nm_ip4_config_set_mss (NMIP4Config *config, guint32 mss)
+{
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	NM_IP4_CONFIG_GET_PRIVATE (config)->mss = mss;
+}
+
+gboolean
+nm_ip4_config_get_never_default (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), FALSE);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->never_default;
+}
+
+void
+nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default)
+{
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	NM_IP4_CONFIG_GET_PRIVATE (config)->never_default = never_default;
+}
+
+void nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis)
+{
+	NMIP4ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+	g_return_if_fail (nis > 0);
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	for (i = 0; i < priv->nis->len; i++) {
+		guint32 s = g_array_index (priv->nis, guint32, i);
+
+		/* No dupes */
+		g_return_if_fail (nis != s);
+	}
+
+	g_array_append_val (priv->nis, nis);
+}
+
+guint32 nm_ip4_config_get_nis_server (NMIP4Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return g_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->nis, guint32, i);
+}
+
+guint32 nm_ip4_config_get_num_nis_servers (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->nis->len;
+}
+
+void nm_ip4_config_reset_nis_servers (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	if (priv->nis->len)
+		g_array_remove_range (priv->nis, 0, priv->nis->len);
+}
+
+void
+nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain)
+{
+	NMIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	g_free (priv->nis_domain);
+	priv->nis_domain = g_strdup (domain);
+}
+
+const char *
+nm_ip4_config_get_nis_domain (NMIP4Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+	return NM_IP4_CONFIG_GET_PRIVATE (config)->nis_domain;
+}
+
+/* libnl convenience/conversion functions */
+
+static int ip4_addr_to_rtnl_local (guint32 ip4_address, struct rtnl_addr *addr)
+{
+	struct nl_addr * local = NULL;
+	int err = 0;
+
+	g_return_val_if_fail (addr != NULL, -1);
+
+	local = nm_utils_ip4_addr_to_nl_addr (ip4_address);
+	err = rtnl_addr_set_local (addr, local);
+	nl_addr_put (local);
+
+	return err;
+}
+
+static int ip4_addr_to_rtnl_peer (guint32 ip4_address, struct rtnl_addr *addr)
+{
+	struct nl_addr * peer = NULL;
+	int err = 0;
+
+	g_return_val_if_fail (addr != NULL, -1);
+
+	peer = nm_utils_ip4_addr_to_nl_addr (ip4_address);
+	err = rtnl_addr_set_peer (addr, peer);
+	nl_addr_put (peer);
+
+	return err;
+}
+
+static int ip4_addr_to_rtnl_broadcast (guint32 ip4_broadcast, struct rtnl_addr *addr)
+{
+	struct nl_addr	* local = NULL;
+	int err = 0;
+
+	g_return_val_if_fail (addr != NULL, -1);
+
+	local = nm_utils_ip4_addr_to_nl_addr (ip4_broadcast);
+	err = rtnl_addr_set_broadcast (addr, local);
+	nl_addr_put (local);
+
+	return err;
+}
+
+
+struct rtnl_addr *
+nm_ip4_config_to_rtnl_addr (NMIP4Config *config, guint32 i, guint32 flags)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	NMIP4Address *config_addr;
+	struct rtnl_addr *addr;
+	gboolean success = TRUE;
+
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+	config_addr = nm_ip4_config_get_address (config, i);
+	g_return_val_if_fail (config_addr != NULL, NULL);
+
+	if (!(addr = rtnl_addr_alloc()))
+		return NULL;
+
+	if (flags & NM_RTNL_ADDR_ADDR)
+		success = (ip4_addr_to_rtnl_local (nm_ip4_address_get_address (config_addr), addr) >= 0);
+
+	if (flags & NM_RTNL_ADDR_PTP_ADDR)
+		success = (ip4_addr_to_rtnl_peer (priv->ptp_address, addr) >= 0);
+
+	if (flags & NM_RTNL_ADDR_PREFIX)
+		rtnl_addr_set_prefixlen (addr, nm_ip4_address_get_prefix (config_addr));
+
+	if (flags & NM_RTNL_ADDR_BROADCAST) {
+		guint32 hostmask, network, bcast, netmask;
+
+		netmask = nm_utils_ip4_prefix_to_netmask (nm_ip4_address_get_prefix (config_addr));
+		network = ntohl (nm_ip4_address_get_address (config_addr)) & ntohl (netmask);
+		hostmask = ~ntohl (netmask);
+		bcast = htonl (network | hostmask);
+
+		success = (ip4_addr_to_rtnl_broadcast (bcast, addr) >= 0);
+	}
+
+	if (!success) {
+		rtnl_addr_put (addr);
+		addr = NULL;
+	}
+
+	return addr;
+}
+
+static gboolean
+addr_slist_compare (GSList *a, GSList *b)
+{
+	GSList *iter_a, *iter_b;
+	gboolean found = FALSE;
+
+	for (iter_a = a; iter_a; iter_a = g_slist_next (iter_a)) {
+		NMIP4Address *addr_a = (NMIP4Address *) iter_a->data;
+
+		for (iter_b = b, found = FALSE; iter_b; iter_b = g_slist_next (iter_b)) {
+			NMIP4Address *addr_b = (NMIP4Address *) iter_b->data;
+
+			if (nm_ip4_address_compare (addr_a, addr_b)) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean
+route_slist_compare (GSList *a, GSList *b)
+{
+	GSList *iter_a, *iter_b;
+	gboolean found = FALSE;
+
+	for (iter_a = a; iter_a; iter_a = g_slist_next (iter_a)) {
+		NMIP4Route *route_a = (NMIP4Route *) iter_a->data;
+
+		for (iter_b = b, found = FALSE; iter_b; iter_b = g_slist_next (iter_b)) {
+			NMIP4Route *route_b = (NMIP4Route *) iter_b->data;
+
+			if (nm_ip4_route_compare (route_a, route_b)) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean
+string_array_compare (GPtrArray *a, GPtrArray *b)
+{
+	int i, j;
+	gboolean found = FALSE;
+
+	for (i = 0; i < a->len; i++) {
+		for (j = 0, found = FALSE; j < b->len; j++) {
+			const char *item_a = g_ptr_array_index (a, i);
+			const char *item_b = g_ptr_array_index (b, j);
+
+			if ((!item_a && !item_b) || (item_a && item_b && !strcmp (item_a, item_b))) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean
+addr_array_compare (GArray *a, GArray *b)
+{
+	int i, j;
+	gboolean found = FALSE;
+
+	for (i = 0; i < a->len; i++) {
+		for (j = 0, found = FALSE; j < b->len; j++) {
+			if (g_array_index (a, guint32, i) == g_array_index (b, guint32, j)) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+NMIP4ConfigCompareFlags
+nm_ip4_config_diff (NMIP4Config *a, NMIP4Config *b)
+{
+	NMIP4ConfigPrivate *a_priv;
+	NMIP4ConfigPrivate *b_priv;
+	NMIP4ConfigCompareFlags flags = NM_IP4_COMPARE_FLAG_NONE;
+
+	if ((a && !b) || (b && !a))
+		return 0xFFFFFFFF;
+	if (!a && !b)
+		return NM_IP4_COMPARE_FLAG_NONE;
+
+	a_priv = NM_IP4_CONFIG_GET_PRIVATE (a);
+	b_priv = NM_IP4_CONFIG_GET_PRIVATE (b);
+
+	if (   !addr_slist_compare (a_priv->addresses, b_priv->addresses)
+	    || !addr_slist_compare (b_priv->addresses, a_priv->addresses))
+		flags |= NM_IP4_COMPARE_FLAG_ADDRESSES;
+
+	if (a_priv->ptp_address != b_priv->ptp_address)
+		flags |= NM_IP4_COMPARE_FLAG_PTP_ADDRESS;
+
+	if (   (a_priv->nameservers->len != b_priv->nameservers->len)
+	    || !addr_array_compare (a_priv->nameservers, b_priv->nameservers)
+	    || !addr_array_compare (b_priv->nameservers, a_priv->nameservers))
+		flags |= NM_IP4_COMPARE_FLAG_NAMESERVERS;
+
+	if (   (a_priv->wins->len != b_priv->wins->len)
+	    || !addr_array_compare (a_priv->wins, b_priv->wins)
+	    || !addr_array_compare (b_priv->wins, a_priv->wins))
+		flags |= NM_IP4_COMPARE_FLAG_WINS_SERVERS;
+
+	if (   (a_priv->nis->len != b_priv->nis->len)
+	    || !addr_array_compare (a_priv->nis, b_priv->nis)
+	    || !addr_array_compare (b_priv->nis, a_priv->nis))
+		flags |= NM_IP4_COMPARE_FLAG_NIS_SERVERS;
+
+	if (   (a_priv->nis_domain || b_priv->nis_domain)
+		&& (g_strcmp0 (a_priv->nis_domain, b_priv->nis_domain) != 0))
+		flags |= NM_IP4_COMPARE_FLAG_NIS_DOMAIN;
+
+	if (   !route_slist_compare (a_priv->routes, b_priv->routes)
+	    || !route_slist_compare (b_priv->routes, a_priv->routes))
+		flags |= NM_IP4_COMPARE_FLAG_ROUTES;
+
+	if (   (a_priv->domains->len != b_priv->domains->len)
+	    || !string_array_compare (a_priv->domains, b_priv->domains)
+	    || !string_array_compare (b_priv->domains, a_priv->domains))
+		flags |= NM_IP4_COMPARE_FLAG_DOMAINS;
+
+	if (   (a_priv->searches->len != b_priv->searches->len)
+	    || !string_array_compare (a_priv->searches, b_priv->searches)
+	    || !string_array_compare (b_priv->searches, a_priv->searches))
+		flags |= NM_IP4_COMPARE_FLAG_SEARCHES;
+
+	if (a_priv->mtu != b_priv->mtu)
+		flags |= NM_IP4_COMPARE_FLAG_MTU;
+
+	if (a_priv->mss != b_priv->mss)
+		flags |= NM_IP4_COMPARE_FLAG_MSS;
+
+	return flags;
+}
+
+static void
+nm_ip4_config_init (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	priv->nameservers = g_array_new (FALSE, TRUE, sizeof (guint32));
+	priv->wins = g_array_new (FALSE, TRUE, sizeof (guint32));
+	priv->domains = g_ptr_array_sized_new (3);
+	priv->searches = g_ptr_array_sized_new (3);
+	priv->nis = g_array_new (FALSE, TRUE, sizeof (guint32));
+}
+
+static void
+finalize (GObject *object)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object);
+
+	nm_utils_slist_free (priv->addresses, (GDestroyNotify) nm_ip4_address_unref);
+	nm_utils_slist_free (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+	g_array_free (priv->wins, TRUE);
+	g_array_free (priv->nameservers, TRUE);
+	g_ptr_array_free (priv->domains, TRUE);
+	g_ptr_array_free (priv->searches, TRUE);
+	g_array_free (priv->nis, TRUE);
+	g_free (priv->nis_domain);
+
+	G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_ADDRESSES:
+		nm_utils_ip4_addresses_to_gvalue (priv->addresses, value);
+		break;
+	case PROP_NAMESERVERS:
+		g_value_set_boxed (value, priv->nameservers);
+		break;
+	case PROP_DOMAINS:
+		g_value_set_boxed (value, priv->domains);
+		break;
+	case PROP_ROUTES:
+		nm_utils_ip4_routes_to_gvalue (priv->routes, value);
+		break;
+	case PROP_WINS_SERVERS:
+		g_value_set_boxed (value, priv->wins);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+	g_type_class_add_private (config_class, sizeof (NMIP4ConfigPrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_ADDRESSES,
+		 g_param_spec_boxed (NM_IP4_CONFIG_ADDRESSES,
+							"Addresses",
+							"IP4 addresses",
+							DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+							G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_NAMESERVERS,
+		 g_param_spec_boxed (NM_IP4_CONFIG_NAMESERVERS,
+							 "Nameservers",
+							 "DNS list",
+							 DBUS_TYPE_G_UINT_ARRAY,
+							 G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_DOMAINS,
+		 g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS,
+							 "Domains",
+							 "Domains",
+							 DBUS_TYPE_G_ARRAY_OF_STRING,
+							 G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_ROUTES,
+		 g_param_spec_boxed (NM_IP4_CONFIG_ROUTES,
+						 "Routes",
+						 "Routes",
+						 DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+						 G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_WINS_SERVERS,
+		 g_param_spec_boxed (NM_IP4_CONFIG_WINS_SERVERS,
+							 "WinsServers",
+							 "WINS server list",
+							 DBUS_TYPE_G_UINT_ARRAY,
+							 G_PARAM_READABLE));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class),
+									 &dbus_glib_nm_ip4_config_object_info);
+}
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
new file mode 100644
index 00000000..2d27acff
--- /dev/null
+++ b/src/nm-ip4-config.h
@@ -0,0 +1,142 @@
+/* -*- 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) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_IP4_CONFIG_H
+#define NM_IP4_CONFIG_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-setting-ip4-config.h"
+
+#define NM_TYPE_IP4_CONFIG            (nm_ip4_config_get_type ())
+#define NM_IP4_CONFIG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP4_CONFIG, NMIP4Config))
+#define NM_IP4_CONFIG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass))
+#define NM_IS_IP4_CONFIG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IP4_CONFIG))
+#define NM_IS_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IP4_CONFIG))
+#define NM_IP4_CONFIG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass))
+
+typedef struct {
+	GObject parent;
+} NMIP4Config;
+
+typedef struct {
+	GObjectClass parent;
+} NMIP4ConfigClass;
+
+#define NM_IP4_CONFIG_ADDRESSES "addresses"
+#define NM_IP4_CONFIG_NAMESERVERS "nameservers"
+#define NM_IP4_CONFIG_DOMAINS "domains"
+#define NM_IP4_CONFIG_ROUTES "routes"
+#define NM_IP4_CONFIG_WINS_SERVERS "wins-servers"
+
+GType nm_ip4_config_get_type (void);
+
+
+NMIP4Config * nm_ip4_config_new                 (void);
+void          nm_ip4_config_export              (NMIP4Config *config);
+const char *  nm_ip4_config_get_dbus_path       (NMIP4Config *config);
+
+void          nm_ip4_config_take_address        (NMIP4Config *config, NMIP4Address *address);
+void          nm_ip4_config_add_address         (NMIP4Config *config, NMIP4Address *address);
+void          nm_ip4_config_replace_address     (NMIP4Config *config, guint32 i, NMIP4Address *new_address);
+NMIP4Address *nm_ip4_config_get_address         (NMIP4Config *config, guint32 i);
+guint32       nm_ip4_config_get_num_addresses   (NMIP4Config *config);
+
+guint32       nm_ip4_config_get_ptp_address     (NMIP4Config *config);
+void          nm_ip4_config_set_ptp_address     (NMIP4Config *config, guint32 ptp_addr);
+
+void          nm_ip4_config_add_nameserver      (NMIP4Config *config, guint32 nameserver);
+guint32       nm_ip4_config_get_nameserver      (NMIP4Config *config, guint i);
+guint32       nm_ip4_config_get_num_nameservers (NMIP4Config *config);
+void          nm_ip4_config_reset_nameservers   (NMIP4Config *config);
+
+void          nm_ip4_config_add_wins            (NMIP4Config *config, guint32 wins);
+guint32       nm_ip4_config_get_wins            (NMIP4Config *config, guint i);
+guint32       nm_ip4_config_get_num_wins        (NMIP4Config *config);
+void          nm_ip4_config_reset_wins          (NMIP4Config *config);
+
+void          nm_ip4_config_take_route          (NMIP4Config *config, NMIP4Route *route);
+void          nm_ip4_config_add_route           (NMIP4Config *config, NMIP4Route *route);
+void          nm_ip4_config_replace_route       (NMIP4Config *config, guint32 i, NMIP4Route *new_route);
+NMIP4Route *  nm_ip4_config_get_route           (NMIP4Config *config, guint32 i);
+guint32       nm_ip4_config_get_num_routes      (NMIP4Config *config);
+void          nm_ip4_config_reset_routes        (NMIP4Config *config);
+
+void          nm_ip4_config_add_domain          (NMIP4Config *config, const char *domain);
+const char *  nm_ip4_config_get_domain          (NMIP4Config *config, guint i);
+guint32       nm_ip4_config_get_num_domains     (NMIP4Config *config);
+void          nm_ip4_config_reset_domains       (NMIP4Config *config);
+
+void          nm_ip4_config_add_search          (NMIP4Config *config, const char *search);
+const char *  nm_ip4_config_get_search          (NMIP4Config *config, guint i);
+guint32       nm_ip4_config_get_num_searches    (NMIP4Config *config);
+void          nm_ip4_config_reset_searches      (NMIP4Config *config);
+
+guint32       nm_ip4_config_get_mtu             (NMIP4Config *config);
+void          nm_ip4_config_set_mtu             (NMIP4Config *config, guint32 mtu);
+
+guint32       nm_ip4_config_get_mss             (NMIP4Config *config);
+void          nm_ip4_config_set_mss             (NMIP4Config *config, guint32 mss);
+
+gboolean      nm_ip4_config_get_never_default   (NMIP4Config *config);
+void          nm_ip4_config_set_never_default   (NMIP4Config *config, gboolean never_default);
+
+void          nm_ip4_config_add_nis_server      (NMIP4Config *config, guint32 nis);
+guint32       nm_ip4_config_get_nis_server      (NMIP4Config *config, guint i);
+guint32       nm_ip4_config_get_num_nis_servers (NMIP4Config *config);
+void          nm_ip4_config_reset_nis_servers   (NMIP4Config *config);
+
+void          nm_ip4_config_set_nis_domain      (NMIP4Config *config, const char *domain);
+const char *  nm_ip4_config_get_nis_domain      (NMIP4Config *config);
+
+
+/* Flags for nm_ip4_config_to_rtnl_addr() */
+#define NM_RTNL_ADDR_NONE		0x0000
+#define NM_RTNL_ADDR_ADDR		0x0001
+#define NM_RTNL_ADDR_PTP_ADDR		0x0002
+#define NM_RTNL_ADDR_PREFIX		0x0004
+#define NM_RTNL_ADDR_BROADCAST	0x0008
+
+#define NM_RTNL_ADDR_DEFAULT		(NM_RTNL_ADDR_ADDR | NM_RTNL_ADDR_PREFIX | NM_RTNL_ADDR_BROADCAST)
+#define NM_RTNL_ADDR_PTP_DEFAULT	(NM_RTNL_ADDR_ADDR | NM_RTNL_ADDR_PREFIX | NM_RTNL_ADDR_PTP_ADDR)
+
+struct rtnl_addr *nm_ip4_config_to_rtnl_addr (NMIP4Config *config, guint32 i, guint32 flags);
+
+typedef enum {
+	NM_IP4_COMPARE_FLAG_NONE        = 0x00000000,  /* match nothing, kinda pointless */
+	NM_IP4_COMPARE_FLAG_ADDRESSES   = 0x00000001,
+	NM_IP4_COMPARE_FLAG_PTP_ADDRESS = 0x00000002,
+	NM_IP4_COMPARE_FLAG_NAMESERVERS = 0x00000004,
+	NM_IP4_COMPARE_FLAG_ROUTES      = 0x00000008,
+	NM_IP4_COMPARE_FLAG_DOMAINS     = 0x00000010,
+	NM_IP4_COMPARE_FLAG_SEARCHES    = 0x00000020,
+	NM_IP4_COMPARE_FLAG_MTU         = 0x00000040,
+	NM_IP4_COMPARE_FLAG_MSS         = 0x00000080,
+	NM_IP4_COMPARE_FLAG_WINS_SERVERS= 0x00000100,
+	NM_IP4_COMPARE_FLAG_NIS_SERVERS = 0x00000200,
+	NM_IP4_COMPARE_FLAG_NIS_DOMAIN  = 0x00000400,
+	NM_IP4_COMPARE_FLAG_ALL         = 0xFFFFFFFF   /* match everything */
+} NMIP4ConfigCompareFlags;
+
+/* Returns a bitfield representing how the two IP4 configs differ */
+NMIP4ConfigCompareFlags nm_ip4_config_diff (NMIP4Config *a, NMIP4Config *b);
+
+#endif /* NM_IP4_CONFIG_H */
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
new file mode 100644
index 00000000..4c6c5c62
--- /dev/null
+++ b/src/nm-ip6-config.c
@@ -0,0 +1,745 @@
+/* -*- 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 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include "nm-ip6-config.h"
+#include "nm-dbus-manager.h"
+#include "NetworkManager.h"
+#include "NetworkManagerUtils.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-utils.h"
+
+#include <netlink/route/addr.h>
+#include <netlink/utils.h>
+#include <netinet/in.h>
+
+#include "nm-ip6-config-glue.h"
+#include "nm-dbus-glib-types.h"
+
+
+G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, G_TYPE_OBJECT)
+
+#define NM_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_CONFIG, NMIP6ConfigPrivate))
+
+typedef struct {
+	char *path;
+
+	GSList *addresses;
+	struct in6_addr ptp_address;
+
+	guint32	mss;	/* Maximum Segment Size of the route */
+
+	GArray *nameservers;
+	GPtrArray *domains;
+	GPtrArray *searches;
+
+	GSList *routes;
+
+	gboolean never_default;
+} NMIP6ConfigPrivate;
+
+
+enum {
+	PROP_0,
+	PROP_ADDRESSES,
+	PROP_NAMESERVERS,
+	PROP_DOMAINS,
+	PROP_ROUTES,
+
+	LAST_PROP
+};
+
+
+static struct nl_addr *
+nm_utils_ip6_addr_to_nl_addr (const struct in6_addr *ip6_addr)
+{
+	struct nl_addr * nla = NULL;
+
+	if (!(nla = nl_addr_alloc (sizeof (struct in6_addr))))
+		return NULL;
+	nl_addr_set_family (nla, AF_INET6);
+	nl_addr_set_binary_addr (nla, (struct in6_addr *)ip6_addr, sizeof (struct in6_addr));
+
+	return nla;
+}
+
+
+NMIP6Config *
+nm_ip6_config_new (void)
+{
+	return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG, NULL);
+}
+
+void
+nm_ip6_config_export (NMIP6Config *config)
+{
+	NMIP6ConfigPrivate *priv;
+	NMDBusManager *dbus_mgr;
+	DBusGConnection *connection;
+	static guint32 counter = 0;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	g_return_if_fail (priv->path == NULL);
+
+	dbus_mgr = nm_dbus_manager_get ();
+	connection = nm_dbus_manager_get_connection (dbus_mgr);
+	priv->path = g_strdup_printf (NM_DBUS_PATH "/IP6Config/%d", counter++);
+
+	dbus_g_connection_register_g_object (connection, priv->path, G_OBJECT (config));
+	g_object_unref (dbus_mgr);
+}
+
+const char *
+nm_ip6_config_get_dbus_path (NMIP6Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), FALSE);
+
+	return NM_IP6_CONFIG_GET_PRIVATE (config)->path;
+}
+
+void
+nm_ip6_config_take_address (NMIP6Config *config, NMIP6Address *address)
+{
+	NMIP6ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+	g_return_if_fail (address != NULL);
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	priv->addresses = g_slist_append (priv->addresses, address);
+}
+
+void
+nm_ip6_config_add_address (NMIP6Config *config,
+                           NMIP6Address *address)
+{
+	NMIP6ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+	g_return_if_fail (address != NULL);
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	priv->addresses = g_slist_append (priv->addresses, nm_ip6_address_dup (address));
+}
+
+void
+nm_ip6_config_replace_address (NMIP6Config *config,
+                               guint i,
+                               NMIP6Address *new_address)
+{
+	NMIP6ConfigPrivate *priv;
+	GSList *old;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	old = g_slist_nth (priv->addresses, i);
+	g_return_if_fail (old != NULL);
+	nm_ip6_address_unref ((NMIP6Address *) old->data);
+
+	old->data = nm_ip6_address_dup (new_address);
+}
+
+NMIP6Address *nm_ip6_config_get_address (NMIP6Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+	return (NMIP6Address *) g_slist_nth_data (NM_IP6_CONFIG_GET_PRIVATE (config)->addresses, i);
+}
+
+guint32 nm_ip6_config_get_num_addresses (NMIP6Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0);
+
+	return g_slist_length (NM_IP6_CONFIG_GET_PRIVATE (config)->addresses);
+}
+
+const struct in6_addr *nm_ip6_config_get_ptp_address (NMIP6Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0);
+
+	return &NM_IP6_CONFIG_GET_PRIVATE (config)->ptp_address;
+}
+
+void nm_ip6_config_set_ptp_address (NMIP6Config *config, const struct in6_addr *ptp_addr)
+{
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	NM_IP6_CONFIG_GET_PRIVATE (config)->ptp_address = *ptp_addr;
+}
+
+void nm_ip6_config_add_nameserver (NMIP6Config *config, const struct in6_addr *nameserver)
+{
+	NMIP6ConfigPrivate *priv;
+	struct in6_addr *nameservers;
+	int i;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+	g_return_if_fail (nameserver != NULL);
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	/* No dupes */
+	nameservers = (struct in6_addr *)priv->nameservers->data;
+	for (i = 0; i < priv->nameservers->len; i++)
+		g_return_if_fail (IN6_ARE_ADDR_EQUAL (nameserver, &nameservers[i]) == FALSE);
+
+	g_array_append_val (priv->nameservers, *nameserver);
+}
+
+const struct in6_addr *nm_ip6_config_get_nameserver (NMIP6Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0);
+
+	return &g_array_index (NM_IP6_CONFIG_GET_PRIVATE (config)->nameservers, struct in6_addr, i);
+}
+
+guint32 nm_ip6_config_get_num_nameservers (NMIP6Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0);
+
+	return NM_IP6_CONFIG_GET_PRIVATE (config)->nameservers->len;
+}
+
+void nm_ip6_config_reset_nameservers (NMIP6Config *config)
+{
+	NMIP6ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	if (priv->nameservers->len)
+		g_array_remove_range (priv->nameservers, 0, priv->nameservers->len);
+}
+
+void
+nm_ip6_config_take_route (NMIP6Config *config, NMIP6Route *route)
+{
+	NMIP6ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+	g_return_if_fail (route != NULL);
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	priv->routes = g_slist_append (priv->routes, route);
+}
+
+void
+nm_ip6_config_add_route (NMIP6Config *config, NMIP6Route *route)
+{
+	NMIP6ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+	g_return_if_fail (route != NULL);
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	priv->routes = g_slist_append (priv->routes, nm_ip6_route_dup (route));
+}
+
+void
+nm_ip6_config_replace_route (NMIP6Config *config,
+							 guint i,
+							 NMIP6Route *new_route)
+{
+	NMIP6ConfigPrivate *priv;
+	GSList *old;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	old = g_slist_nth (priv->routes, i);
+	g_return_if_fail (old != NULL);
+	nm_ip6_route_unref ((NMIP6Route *) old->data);
+
+	old->data = nm_ip6_route_dup (new_route);
+}
+
+NMIP6Route *
+nm_ip6_config_get_route (NMIP6Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+	return (NMIP6Route *) g_slist_nth_data (NM_IP6_CONFIG_GET_PRIVATE (config)->routes, i);
+}
+
+guint32 nm_ip6_config_get_num_routes (NMIP6Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0);
+
+	return g_slist_length (NM_IP6_CONFIG_GET_PRIVATE (config)->routes);
+}
+
+void nm_ip6_config_reset_routes (NMIP6Config *config)
+{
+	NMIP6ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	g_slist_foreach (priv->routes, (GFunc) g_free, NULL);
+	priv->routes = NULL;
+}
+
+void nm_ip6_config_add_domain (NMIP6Config *config, const char *domain)
+{
+	NMIP6ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+	g_return_if_fail (domain != NULL);
+	g_return_if_fail (strlen (domain) > 0);
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	for (i = 0; i < priv->domains->len; i++) {
+		if (!strcmp (g_ptr_array_index (priv->domains, i), domain))
+			return;
+	}
+
+	g_ptr_array_add (priv->domains, g_strdup (domain));
+}
+
+const char *nm_ip6_config_get_domain (NMIP6Config *config, guint i)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+	return (const char *) g_ptr_array_index (NM_IP6_CONFIG_GET_PRIVATE (config)->domains, i);
+}
+
+guint32 nm_ip6_config_get_num_domains (NMIP6Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0);
+
+	return NM_IP6_CONFIG_GET_PRIVATE (config)->domains->len;
+}
+
+void nm_ip6_config_reset_domains (NMIP6Config *config)
+{
+	NMIP6ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	for (i = 0; i < priv->domains->len; i++)
+		g_free (g_ptr_array_index (priv->domains, i));
+	g_ptr_array_free (priv->domains, TRUE);
+	priv->domains = g_ptr_array_sized_new (3);
+}
+
+void nm_ip6_config_add_search (NMIP6Config *config, const char *search)
+{
+	NMIP6ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (config != NULL);
+	g_return_if_fail (search != NULL);
+	g_return_if_fail (strlen (search) > 0);
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	for (i = 0; i < priv->searches->len; i++) {
+		if (!strcmp (g_ptr_array_index (priv->searches, i), search))
+			return;
+	}
+
+	g_ptr_array_add (priv->searches, g_strdup (search));
+}
+
+const char *nm_ip6_config_get_search (NMIP6Config *config, guint i)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return (const char *) g_ptr_array_index (NM_IP6_CONFIG_GET_PRIVATE (config)->searches, i);
+}
+
+guint32 nm_ip6_config_get_num_searches (NMIP6Config *config)
+{
+	g_return_val_if_fail (config != NULL, 0);
+
+	return NM_IP6_CONFIG_GET_PRIVATE (config)->searches->len;
+}
+
+void nm_ip6_config_reset_searches (NMIP6Config *config)
+{
+	NMIP6ConfigPrivate *priv;
+	int i;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	for (i = 0; i < priv->searches->len; i++)
+		g_free (g_ptr_array_index (priv->searches, i));
+	g_ptr_array_free (priv->searches, TRUE);
+	priv->searches = g_ptr_array_sized_new (3);
+}
+
+guint32 nm_ip6_config_get_mss (NMIP6Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0);
+
+	return NM_IP6_CONFIG_GET_PRIVATE (config)->mss;
+}
+
+void nm_ip6_config_set_mss (NMIP6Config *config, guint32 mss)
+{
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	NM_IP6_CONFIG_GET_PRIVATE (config)->mss = mss;
+}
+
+gboolean
+nm_ip6_config_get_never_default (NMIP6Config *config)
+{
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), FALSE);
+
+	return NM_IP6_CONFIG_GET_PRIVATE (config)->never_default;
+}
+
+void
+nm_ip6_config_set_never_default (NMIP6Config *config, gboolean never_default)
+{
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+
+	NM_IP6_CONFIG_GET_PRIVATE (config)->never_default = never_default;
+}
+
+/* libnl convenience/conversion functions */
+
+static int ip6_addr_to_rtnl_local (const struct in6_addr *ip6_address, struct rtnl_addr *addr)
+{
+	struct nl_addr * local = NULL;
+	int err = 0;
+
+	g_return_val_if_fail (addr != NULL, -1);
+
+	local = nm_utils_ip6_addr_to_nl_addr (ip6_address);
+	err = rtnl_addr_set_local (addr, local);
+	nl_addr_put (local);
+
+	return err;
+}
+
+static int ip6_addr_to_rtnl_peer (const struct in6_addr *ip6_address, struct rtnl_addr *addr)
+{
+	struct nl_addr * peer = NULL;
+	int err = 0;
+
+	g_return_val_if_fail (addr != NULL, -1);
+
+	peer = nm_utils_ip6_addr_to_nl_addr (ip6_address);
+	err = rtnl_addr_set_peer (addr, peer);
+	nl_addr_put (peer);
+
+	return err;
+}
+
+struct rtnl_addr *
+nm_ip6_config_to_rtnl_addr (NMIP6Config *config, guint32 i, guint32 flags)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	NMIP6Address *config_addr;
+	struct rtnl_addr *addr;
+	gboolean success = TRUE;
+
+	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+	config_addr = nm_ip6_config_get_address (config, i);
+	g_return_val_if_fail (config_addr != NULL, NULL);
+
+	if (!(addr = rtnl_addr_alloc()))
+		return NULL;
+
+	if (flags & NM_RTNL_ADDR_ADDR)
+		success = (ip6_addr_to_rtnl_local (nm_ip6_address_get_address (config_addr), addr) >= 0);
+
+	if (flags & NM_RTNL_ADDR_PTP_ADDR)
+		success = (ip6_addr_to_rtnl_peer (&priv->ptp_address, addr) >= 0);
+
+	if (flags & NM_RTNL_ADDR_PREFIX)
+		rtnl_addr_set_prefixlen (addr, nm_ip6_address_get_prefix (config_addr));
+
+	if (!success) {
+		rtnl_addr_put (addr);
+		addr = NULL;
+	}
+
+	return addr;
+}
+
+static gboolean
+addr_slist_compare (GSList *a, GSList *b)
+{
+	GSList *iter_a, *iter_b;
+	gboolean found = FALSE;
+
+	for (iter_a = a; iter_a; iter_a = g_slist_next (iter_a)) {
+		NMIP6Address *addr_a = (NMIP6Address *) iter_a->data;
+
+		for (iter_b = b, found = FALSE; iter_b; iter_b = g_slist_next (iter_b)) {
+			NMIP6Address *addr_b = (NMIP6Address *) iter_b->data;
+
+			if (nm_ip6_address_compare (addr_a, addr_b)) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean
+route_slist_compare (GSList *a, GSList *b)
+{
+	GSList *iter_a, *iter_b;
+	gboolean found = FALSE;
+
+	for (iter_a = a; iter_a; iter_a = g_slist_next (iter_a)) {
+		NMIP6Route *route_a = (NMIP6Route *) iter_a->data;
+
+		for (iter_b = b, found = FALSE; iter_b; iter_b = g_slist_next (iter_b)) {
+			NMIP6Route *route_b = (NMIP6Route *) iter_b->data;
+
+			if (nm_ip6_route_compare (route_a, route_b)) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean
+string_array_compare (GPtrArray *a, GPtrArray *b)
+{
+	int i, j;
+	gboolean found = FALSE;
+
+	for (i = 0; i < a->len; i++) {
+		for (j = 0, found = FALSE; j < b->len; j++) {
+			const char *item_a = g_ptr_array_index (a, i);
+			const char *item_b = g_ptr_array_index (b, j);
+
+			if ((!item_a && !item_b) || (item_a && item_b && !strcmp (item_a, item_b))) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean
+addr_array_compare (GArray *a, GArray *b)
+{
+	struct in6_addr *addrs_a, *addrs_b;
+	int i, j;
+	gboolean found = FALSE;
+
+	addrs_a = (struct in6_addr *)a->data;
+	addrs_b = (struct in6_addr *)b->data;
+	for (i = 0; i < a->len; i++) {
+		for (j = 0, found = FALSE; j < b->len; j++) {
+			if (IN6_ARE_ADDR_EQUAL (&addrs_a[i], &addrs_b[j])) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+NMIP6ConfigCompareFlags
+nm_ip6_config_diff (NMIP6Config *a, NMIP6Config *b)
+{
+	NMIP6ConfigPrivate *a_priv;
+	NMIP6ConfigPrivate *b_priv;
+	NMIP6ConfigCompareFlags flags = NM_IP6_COMPARE_FLAG_NONE;
+
+	if ((a && !b) || (b && !a))
+		return 0xFFFFFFFF;
+	if (!a && !b)
+		return NM_IP6_COMPARE_FLAG_NONE;
+
+	a_priv = NM_IP6_CONFIG_GET_PRIVATE (a);
+	b_priv = NM_IP6_CONFIG_GET_PRIVATE (b);
+
+	if (   !addr_slist_compare (a_priv->addresses, b_priv->addresses)
+	    || !addr_slist_compare (b_priv->addresses, a_priv->addresses))
+		flags |= NM_IP6_COMPARE_FLAG_ADDRESSES;
+
+	if (memcmp (&a_priv->ptp_address, &b_priv->ptp_address, sizeof (struct in6_addr)) != 0)
+		flags |= NM_IP6_COMPARE_FLAG_PTP_ADDRESS;
+
+	if (   (a_priv->nameservers->len != b_priv->nameservers->len)
+	    || !addr_array_compare (a_priv->nameservers, b_priv->nameservers)
+	    || !addr_array_compare (b_priv->nameservers, a_priv->nameservers))
+		flags |= NM_IP6_COMPARE_FLAG_NAMESERVERS;
+
+	if (   !route_slist_compare (a_priv->routes, b_priv->routes)
+	    || !route_slist_compare (b_priv->routes, a_priv->routes))
+		flags |= NM_IP6_COMPARE_FLAG_ROUTES;
+
+	if (   (a_priv->domains->len != b_priv->domains->len)
+	    || !string_array_compare (a_priv->domains, b_priv->domains)
+	    || !string_array_compare (b_priv->domains, a_priv->domains))
+		flags |= NM_IP6_COMPARE_FLAG_DOMAINS;
+
+	if (   (a_priv->searches->len != b_priv->searches->len)
+	    || !string_array_compare (a_priv->searches, b_priv->searches)
+	    || !string_array_compare (b_priv->searches, a_priv->searches))
+		flags |= NM_IP6_COMPARE_FLAG_SEARCHES;
+
+	if (a_priv->mss != b_priv->mss)
+		flags |= NM_IP6_COMPARE_FLAG_MSS;
+
+	return flags;
+}
+
+static void
+nm_ip6_config_init (NMIP6Config *config)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	priv->nameservers = g_array_new (FALSE, TRUE, sizeof (struct in6_addr));
+	priv->domains = g_ptr_array_sized_new (3);
+	priv->searches = g_ptr_array_sized_new (3);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object);
+
+	nm_utils_slist_free (priv->addresses, (GDestroyNotify) nm_ip6_address_unref);
+	nm_utils_slist_free (priv->routes, (GDestroyNotify) nm_ip6_route_unref);
+	g_array_free (priv->nameservers, TRUE);
+	g_ptr_array_free (priv->domains, TRUE);
+	g_ptr_array_free (priv->searches, TRUE);
+
+	G_OBJECT_CLASS (nm_ip6_config_parent_class)->finalize (object);
+}
+
+static void
+nameservers_to_gvalue (GArray *array, GValue *value)
+{
+	GPtrArray *dns;
+	guint i = 0;
+
+	dns = g_ptr_array_new ();
+
+	while (array && (i < array->len)) {
+		struct in6_addr *addr;
+		GByteArray *bytearray;
+		addr = &g_array_index (array, struct in6_addr, i++);
+
+		bytearray = g_byte_array_sized_new (16);
+		g_byte_array_append (bytearray, (guint8 *) addr->s6_addr, 16);
+		g_ptr_array_add (dns, bytearray);
+	}
+
+	g_value_take_boxed (value, dns);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_ADDRESSES:
+		nm_utils_ip6_addresses_to_gvalue (priv->addresses, value);
+		break;
+	case PROP_NAMESERVERS:
+		nameservers_to_gvalue (priv->nameservers, value);
+		break;
+	case PROP_DOMAINS:
+		g_value_set_boxed (value, priv->domains);
+		break;
+	case PROP_ROUTES:
+		nm_utils_ip6_routes_to_gvalue (priv->routes, value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+	g_type_class_add_private (config_class, sizeof (NMIP6ConfigPrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	/* properties */
+	g_object_class_install_property (object_class, PROP_ADDRESSES,
+		g_param_spec_boxed (NM_IP6_CONFIG_ADDRESSES,
+		                    "Addresses",
+		                    "IP6 addresses",
+		                    DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
+		                    G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_NAMESERVERS,
+		g_param_spec_boxed (NM_IP6_CONFIG_NAMESERVERS,
+		                    "Nameservers",
+		                    "DNS list",
+		                    DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR,
+		                    G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_DOMAINS,
+		g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS,
+		                    "Domains",
+		                    "Domains",
+		                    DBUS_TYPE_G_ARRAY_OF_STRING,
+		                    G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_ROUTES,
+		g_param_spec_boxed (NM_IP6_CONFIG_ROUTES,
+		                    "Routes",
+		                    "Routes",
+		                    DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE,
+		                    G_PARAM_READABLE));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (config_class),
+									 &dbus_glib_nm_ip6_config_object_info);
+}
diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
new file mode 100644
index 00000000..1e75a121
--- /dev/null
+++ b/src/nm-ip6-config.h
@@ -0,0 +1,120 @@
+/* -*- 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) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_IP6_CONFIG_H
+#define NM_IP6_CONFIG_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-setting-ip6-config.h"
+
+#define NM_TYPE_IP6_CONFIG            (nm_ip6_config_get_type ())
+#define NM_IP6_CONFIG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP6_CONFIG, NMIP6Config))
+#define NM_IP6_CONFIG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass))
+#define NM_IS_IP6_CONFIG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IP6_CONFIG))
+#define NM_IS_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_IP6_CONFIG))
+#define NM_IP6_CONFIG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass))
+
+typedef struct {
+	GObject parent;
+} NMIP6Config;
+
+typedef struct {
+	GObjectClass parent;
+} NMIP6ConfigClass;
+
+#define NM_IP6_CONFIG_ADDRESSES "addresses"
+#define NM_IP6_CONFIG_NAMESERVERS "nameservers"
+#define NM_IP6_CONFIG_DOMAINS "domains"
+#define NM_IP6_CONFIG_ROUTES "routes"
+
+GType nm_ip6_config_get_type (void);
+
+
+NMIP6Config * nm_ip6_config_new                 (void);
+void          nm_ip6_config_export              (NMIP6Config *config);
+const char *  nm_ip6_config_get_dbus_path       (NMIP6Config *config);
+
+void          nm_ip6_config_take_address        (NMIP6Config *config, NMIP6Address *address);
+void          nm_ip6_config_add_address         (NMIP6Config *config, NMIP6Address *address);
+void          nm_ip6_config_replace_address     (NMIP6Config *config, guint32 i, NMIP6Address *new_address);
+NMIP6Address *nm_ip6_config_get_address         (NMIP6Config *config, guint32 i);
+guint32       nm_ip6_config_get_num_addresses   (NMIP6Config *config);
+
+const struct in6_addr *nm_ip6_config_get_ptp_address (NMIP6Config *config);
+void          nm_ip6_config_set_ptp_address     (NMIP6Config *config, const struct in6_addr *ptp_addr);
+
+void          nm_ip6_config_add_nameserver      (NMIP6Config *config, const struct in6_addr *nameserver);
+const struct in6_addr *nm_ip6_config_get_nameserver      (NMIP6Config *config, guint i);
+guint32       nm_ip6_config_get_num_nameservers (NMIP6Config *config);
+void          nm_ip6_config_reset_nameservers   (NMIP6Config *config);
+
+void          nm_ip6_config_take_route          (NMIP6Config *config, NMIP6Route *route);
+void          nm_ip6_config_add_route           (NMIP6Config *config, NMIP6Route *route);
+void          nm_ip6_config_replace_route       (NMIP6Config *config, guint32 i, NMIP6Route *new_route);
+NMIP6Route *  nm_ip6_config_get_route           (NMIP6Config *config, guint32 i);
+guint32       nm_ip6_config_get_num_routes      (NMIP6Config *config);
+void          nm_ip6_config_reset_routes        (NMIP6Config *config);
+
+void          nm_ip6_config_add_domain          (NMIP6Config *config, const char *domain);
+const char *  nm_ip6_config_get_domain          (NMIP6Config *config, guint i);
+guint32       nm_ip6_config_get_num_domains     (NMIP6Config *config);
+void          nm_ip6_config_reset_domains       (NMIP6Config *config);
+
+void          nm_ip6_config_add_search          (NMIP6Config *config, const char *search);
+const char *  nm_ip6_config_get_search          (NMIP6Config *config, guint i);
+guint32       nm_ip6_config_get_num_searches    (NMIP6Config *config);
+void          nm_ip6_config_reset_searches      (NMIP6Config *config);
+
+guint32       nm_ip6_config_get_mss             (NMIP6Config *config);
+void          nm_ip6_config_set_mss             (NMIP6Config *config, guint32 mss);
+
+gboolean      nm_ip6_config_get_never_default   (NMIP6Config *config);
+void          nm_ip6_config_set_never_default   (NMIP6Config *config, gboolean never_default);
+
+/* Flags for nm_ip6_config_to_rtnl_addr() */
+#define NM_RTNL_ADDR_NONE		0x0000
+#define NM_RTNL_ADDR_ADDR		0x0001
+#define NM_RTNL_ADDR_PTP_ADDR		0x0002
+#define NM_RTNL_ADDR_PREFIX		0x0004
+#define NM_RTNL_ADDR_BROADCAST	0x0008
+
+#define NM_RTNL_ADDR_DEFAULT		(NM_RTNL_ADDR_ADDR | NM_RTNL_ADDR_PREFIX | NM_RTNL_ADDR_BROADCAST)
+#define NM_RTNL_ADDR_PTP_DEFAULT	(NM_RTNL_ADDR_ADDR | NM_RTNL_ADDR_PREFIX | NM_RTNL_ADDR_PTP_ADDR)
+
+struct rtnl_addr *nm_ip6_config_to_rtnl_addr (NMIP6Config *config, guint32 i, guint32 flags);
+
+typedef enum {
+	NM_IP6_COMPARE_FLAG_NONE        = 0x00000000,  /* match nothing, kinda pointless */
+	NM_IP6_COMPARE_FLAG_ADDRESSES   = 0x00000001,
+	NM_IP6_COMPARE_FLAG_PTP_ADDRESS = 0x00000002,
+	NM_IP6_COMPARE_FLAG_NAMESERVERS = 0x00000004,
+	NM_IP6_COMPARE_FLAG_ROUTES      = 0x00000008,
+	NM_IP6_COMPARE_FLAG_DOMAINS     = 0x00000010,
+	NM_IP6_COMPARE_FLAG_SEARCHES    = 0x00000020,
+	NM_IP6_COMPARE_FLAG_MSS         = 0x00000080,
+	NM_IP6_COMPARE_FLAG_ALL         = 0xFFFFFFFF   /* match everything */
+} NMIP6ConfigCompareFlags;
+
+/* Returns a bitfield representing how the two IP6 configs differ */
+NMIP6ConfigCompareFlags nm_ip6_config_diff (NMIP6Config *a, NMIP6Config *b);
+
+#endif /* NM_IP6_CONFIG_H */
diff --git a/src/nm-manager.c b/src/nm-manager.c
new file mode 100644
index 00000000..7d46285b
--- /dev/null
+++ b/src/nm-manager.c
@@ -0,0 +1,3463 @@
+/* -*- 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) 2007 - 2009 Novell, Inc.
+ * Copyright (C) 2007 - 2010 Red Hat, Inc.
+ */
+
+#include <netinet/ether.h>
+#include <string.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib.h>
+
+#include "nm-glib-compat.h"
+#include "nm-manager.h"
+#include "nm-logging.h"
+#include "nm-dbus-manager.h"
+#include "nm-vpn-manager.h"
+#include "nm-modem-manager.h"
+#include "nm-device-bt.h"
+#include "nm-device-interface.h"
+#include "nm-device-private.h"
+#include "nm-device-ethernet.h"
+#include "nm-device-wifi.h"
+#include "nm-device-olpc-mesh.h"
+#include "nm-device-cdma.h"
+#include "nm-device-gsm.h"
+#include "nm-system.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-setting-bluetooth.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-vpn.h"
+#include "nm-marshal.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-udev-manager.h"
+#include "nm-hostname-provider.h"
+#include "nm-bluez-manager.h"
+#include "nm-bluez-common.h"
+#include "nm-sysconfig-settings.h"
+#include "nm-secrets-provider-interface.h"
+#include "nm-settings-interface.h"
+#include "nm-settings-system-interface.h"
+
+#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
+#define NM_AUTOIP_DBUS_IFACE   "org.freedesktop.nm_avahi_autoipd"
+
+static gboolean impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err);
+static void impl_manager_activate_connection (NMManager *manager,
+								  const char *service_name,
+								  const char *connection_path,
+								  const char *device_path,
+								  const char *specific_object_path,
+								  DBusGMethodInvocation *context);
+
+static gboolean impl_manager_deactivate_connection (NMManager *manager,
+                                                    const char *connection_path,
+                                                    GError **error);
+
+static gboolean impl_manager_sleep (NMManager *manager, gboolean sleep, GError **err);
+
+static gboolean impl_manager_enable (NMManager *manager, gboolean enable, GError **err);
+
+static gboolean impl_manager_set_logging (NMManager *manager,
+                                          const char *level,
+                                          const char *domains,
+                                          GError **error);
+
+/* Legacy 0.6 compatibility interface */
+
+static gboolean impl_manager_legacy_sleep (NMManager *manager, GError **err);
+static gboolean impl_manager_legacy_wake  (NMManager *manager, GError **err);
+static gboolean impl_manager_legacy_state (NMManager *manager, guint32 *state, GError **err);
+
+#include "nm-manager-glue.h"
+
+static void user_destroy_connections (NMManager *manager);
+
+static void connection_added_default_handler (NMManager *manager,
+									 NMConnection *connection,
+									 NMConnectionScope scope);
+
+static void udev_device_added_cb (NMUdevManager *udev_mgr,
+                                  GUdevDevice *device,
+                                  NMDeviceCreatorFn creator_fn,
+                                  gpointer user_data);
+
+static void udev_device_removed_cb (NMUdevManager *udev_mgr,
+                                    GUdevDevice *device,
+                                    gpointer user_data);
+
+static void bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr,
+					   const char *bdaddr,
+					   const char *name,
+					   const char *object_path,
+					   guint32 uuids,
+					   NMManager *manager);
+
+static void bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr,
+					     const char *bdaddr,
+					     const char *object_path,
+					     gpointer user_data);
+
+static void bluez_manager_resync_devices (NMManager *self);
+
+static void add_device (NMManager *self, NMDevice *device);
+
+static void hostname_provider_init (NMHostnameProvider *provider_class);
+
+static const char *internal_activate_device (NMManager *manager,
+                                             NMDevice *device,
+                                             NMConnection *connection,
+                                             const char *specific_object,
+                                             gboolean user_requested,
+                                             gboolean assumed,
+                                             GError **error);
+
+static NMDevice *find_device_by_iface (NMManager *self, const gchar *iface);
+
+static GSList * remove_one_device (NMManager *manager,
+                                   GSList *list,
+                                   NMDevice *device,
+                                   gboolean quitting,
+                                   gboolean force_unmanage);
+
+static NMDevice *nm_manager_get_device_by_udi (NMManager *manager, const char *udi);
+
+#define SSD_POKE_INTERVAL 120
+#define ORIGDEV_TAG "originating-device"
+
+typedef struct {
+	DBusGMethodInvocation *context;
+	NMConnectionScope scope;
+	char *connection_path;
+	char *specific_object_path;
+	char *device_path;
+	guint timeout_id;
+} PendingConnectionInfo;
+
+typedef struct {
+	gboolean enabled;
+	gboolean hw_enabled;
+	RfKillType rtype;
+	const char *desc;
+	const char *key;
+	const char *prop;
+	const char *hw_prop;
+	RfKillState (*other_enabled_func) (NMManager *);
+} RadioState;
+
+typedef struct {
+	char *config_file;
+	char *state_file;
+
+	GSList *devices;
+	NMState state;
+
+	NMDBusManager *dbus_mgr;
+	NMUdevManager *udev_mgr;
+	NMBluezManager *bluez_mgr;
+
+	GHashTable *user_connections;
+	DBusGProxy *user_proxy;
+
+	GHashTable *system_connections;
+	NMSysconfigSettings *sys_settings;
+	char *hostname;
+
+	GSList *secrets_calls;
+
+	PendingConnectionInfo *pending_connection_info;
+
+	RadioState radio_states[RFKILL_TYPE_MAX];
+	gboolean sleeping;
+	gboolean net_enabled;
+
+	NMVPNManager *vpn_manager;
+	guint vpn_manager_id;
+
+	NMModemManager *modem_manager;
+	guint modem_added_id;
+	guint modem_removed_id;
+
+	DBusGProxy *aipd_proxy;
+
+	gboolean disposed;
+} NMManagerPrivate;
+
+#define NM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MANAGER, NMManagerPrivate))
+
+G_DEFINE_TYPE_EXTENDED (NMManager, nm_manager, G_TYPE_OBJECT, 0,
+						G_IMPLEMENT_INTERFACE (NM_TYPE_HOSTNAME_PROVIDER,
+											   hostname_provider_init))
+
+enum {
+	DEVICE_ADDED,
+	DEVICE_REMOVED,
+	STATE_CHANGED,
+	STATE_CHANGE,  /* DEPRECATED */
+	PROPERTIES_CHANGED,
+	CONNECTIONS_ADDED,
+	CONNECTION_ADDED,
+	CONNECTION_UPDATED,
+	CONNECTION_REMOVED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_0,
+	PROP_STATE,
+	PROP_NETWORKING_ENABLED,
+	PROP_WIRELESS_ENABLED,
+	PROP_WIRELESS_HARDWARE_ENABLED,
+	PROP_WWAN_ENABLED,
+	PROP_WWAN_HARDWARE_ENABLED,
+	PROP_ACTIVE_CONNECTIONS,
+
+	/* Not exported */
+	PROP_HOSTNAME,
+	PROP_SLEEPING,
+
+	LAST_PROP
+};
+
+typedef enum
+{
+	NM_MANAGER_ERROR_UNKNOWN_CONNECTION = 0,
+	NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+	NM_MANAGER_ERROR_UNMANAGED_DEVICE,
+	NM_MANAGER_ERROR_INVALID_SERVICE,
+	NM_MANAGER_ERROR_SYSTEM_CONNECTION,
+	NM_MANAGER_ERROR_PERMISSION_DENIED,
+	NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
+	NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE,
+	NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED,
+} NMManagerError;
+
+#define NM_MANAGER_ERROR (nm_manager_error_quark ())
+#define NM_TYPE_MANAGER_ERROR (nm_manager_error_get_type ()) 
+
+static GQuark
+nm_manager_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-manager-error");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+static GType
+nm_manager_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Connection was not provided by any known settings service. */
+			ENUM_ENTRY (NM_MANAGER_ERROR_UNKNOWN_CONNECTION, "UnknownConnection"),
+			/* Unknown device. */
+			ENUM_ENTRY (NM_MANAGER_ERROR_UNKNOWN_DEVICE, "UnknownDevice"),
+			/* Unmanaged device. */
+			ENUM_ENTRY (NM_MANAGER_ERROR_UNMANAGED_DEVICE, "UnmanagedDevice"),
+			/* Invalid settings service (not a recognized system or user
+			 * settings service name)
+			 */
+			ENUM_ENTRY (NM_MANAGER_ERROR_INVALID_SERVICE, "InvalidService"),
+			/* Connection was superceded by a system connection. */
+			ENUM_ENTRY (NM_MANAGER_ERROR_SYSTEM_CONNECTION, "SystemConnection"),
+			/* User does not have the permission to activate this connection. */
+			ENUM_ENTRY (NM_MANAGER_ERROR_PERMISSION_DENIED, "PermissionDenied"),
+			/* The connection was not active. */
+			ENUM_ENTRY (NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, "ConnectionNotActive"),
+			/* The manager is already in the requested sleep state */
+			ENUM_ENTRY (NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, "AlreadyAsleepOrAwake"),
+			/* The manager is already in the requested enabled/disabled state */
+			ENUM_ENTRY (NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, "AlreadyEnabledOrDisabled"),
+			{ 0, 0, 0 },
+		};
+		etype = g_enum_register_static ("NMManagerError", values);
+	}
+	return etype;
+}
+
+static gboolean
+manager_sleeping (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	if (priv->sleeping || !priv->net_enabled)
+		return TRUE;
+	return FALSE;
+}
+
+static void
+vpn_manager_connection_deactivated_cb (NMVPNManager *manager,
+                                       NMVPNConnection *vpn,
+                                       NMVPNConnectionState state,
+                                       NMVPNConnectionStateReason reason,
+                                       gpointer user_data)
+{
+	g_object_notify (G_OBJECT (user_data), NM_MANAGER_ACTIVE_CONNECTIONS);
+}
+
+static void
+modem_added (NMModemManager *modem_manager,
+			 NMModem *modem,
+			 const char *driver,
+			 gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *replace_device, *device = NULL;
+	const char *ip_iface;
+	GSList *iter;
+
+	ip_iface = nm_modem_get_iface (modem);
+
+	replace_device = find_device_by_iface (NM_MANAGER (user_data), ip_iface);
+	if (replace_device) {
+		priv->devices = remove_one_device (NM_MANAGER (user_data),
+		                                   priv->devices,
+		                                   replace_device,
+		                                   FALSE,
+		                                   TRUE);
+	}
+
+	/* Give Bluetooth DUN devices first chance to claim the modem */
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		if (NM_IS_DEVICE_BT (iter->data)) {
+			if (nm_device_bt_modem_added (NM_DEVICE_BT (iter->data), modem, driver))
+				return;
+		}
+	}
+
+	/* If it was a Bluetooth modem and no bluetooth device claimed it, ignore
+	 * it.  The rfcomm port (and thus the modem) gets created automatically
+	 * by the Bluetooth code during the connection process.
+	 */
+	if (driver && !strcmp (driver, "bluetooth")) {
+		nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", ip_iface);
+		return;
+	}
+
+	/* Otherwise make a new top-level NMDevice for it */
+	if (NM_IS_MODEM_GSM (modem))
+		device = nm_device_gsm_new (NM_MODEM_GSM (modem), driver);
+	else if (NM_IS_MODEM_CDMA (modem))
+		device = nm_device_cdma_new (NM_MODEM_CDMA (modem), driver);
+	else
+		nm_log_info (LOGD_MB, "unhandled modem '%s'", ip_iface);
+
+	if (device)
+		add_device (self, device);
+}
+
+static void
+nm_manager_update_state (NMManager *manager)
+{
+	NMManagerPrivate *priv;
+	NMState new_state = NM_STATE_DISCONNECTED;
+
+	g_return_if_fail (NM_IS_MANAGER (manager));
+
+	priv = NM_MANAGER_GET_PRIVATE (manager);
+
+	if (manager_sleeping (manager))
+		new_state = NM_STATE_ASLEEP;
+	else {
+		GSList *iter;
+
+		for (iter = priv->devices; iter; iter = iter->next) {
+			NMDevice *dev = NM_DEVICE (iter->data);
+
+			if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) {
+				new_state = NM_STATE_CONNECTED;
+				break;
+			} else if (nm_device_is_activating (dev)) {
+				new_state = NM_STATE_CONNECTING;
+			}
+		}
+	}
+
+	if (priv->state != new_state) {
+		priv->state = new_state;
+		g_object_notify (G_OBJECT (manager), NM_MANAGER_STATE);
+
+		g_signal_emit (manager, signals[STATE_CHANGED], 0, priv->state);
+
+		/* Emit StateChange too for backwards compatibility */
+		g_signal_emit (manager, signals[STATE_CHANGE], 0, priv->state);
+	}
+}
+
+static void
+manager_device_state_changed (NMDevice *device,
+                              NMDeviceState new_state,
+                              NMDeviceState old_state,
+                              NMDeviceStateReason reason,
+                              gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+
+	switch (new_state) {
+	case NM_DEVICE_STATE_UNMANAGED:
+	case NM_DEVICE_STATE_UNAVAILABLE:
+	case NM_DEVICE_STATE_DISCONNECTED:
+	case NM_DEVICE_STATE_PREPARE:
+	case NM_DEVICE_STATE_FAILED:
+		g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS);
+		break;
+	default:
+		break;
+	}
+
+	nm_manager_update_state (manager);
+}
+
+/* Removes a device from a device list; returns the start of the new device list */
+static GSList *
+remove_one_device (NMManager *manager,
+                   GSList *list,
+                   NMDevice *device,
+                   gboolean quitting,
+                   gboolean force_unmanage)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+
+	if (nm_device_get_managed (device)) {
+		gboolean unmanage = !quitting;
+
+		/* Don't unmanage active assume-connection-capable devices at shutdown */
+		if (   nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device))
+		    && nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
+			unmanage = FALSE;
+
+		if (unmanage || force_unmanage)
+			nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED);
+	}
+
+	g_signal_handlers_disconnect_by_func (device, manager_device_state_changed, manager);
+
+	nm_sysconfig_settings_device_removed (priv->sys_settings, device);
+	g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device);
+	g_object_unref (device);
+
+	return g_slist_remove (list, device);
+}
+
+static void
+modem_removed (NMModemManager *modem_manager,
+			   NMModem *modem,
+			   gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *found;
+	GSList *iter;
+
+	/* Give Bluetooth DUN devices first chance to handle the modem removal */
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		if (NM_IS_DEVICE_BT (iter->data)) {
+			if (nm_device_bt_modem_removed (NM_DEVICE_BT (iter->data), modem))
+				return;
+		}
+	}
+
+	/* Otherwise remove the standalone modem */
+	found = nm_manager_get_device_by_udi (self, nm_modem_get_path (modem));
+	if (found)
+		priv->devices = remove_one_device (self, priv->devices, found, FALSE, TRUE);
+}
+
+static void
+aipd_handle_event (DBusGProxy *proxy,
+                   const char *event,
+                   const char *iface,
+                   const char *address,
+                   gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	GSList *iter;
+	gboolean handled = FALSE;
+
+	if (!event || !iface) {
+		nm_log_warn (LOGD_AUTOIP4, "incomplete message received from avahi-autoipd");
+		return;
+	}
+
+	if (   (strcmp (event, "BIND") != 0)
+	    && (strcmp (event, "CONFLICT") != 0)
+	    && (strcmp (event, "UNBIND") != 0)
+	    && (strcmp (event, "STOP") != 0)) {
+		nm_log_warn (LOGD_AUTOIP4, "unknown event '%s' received from avahi-autoipd", event);
+		return;
+	}
+
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *candidate = NM_DEVICE (iter->data);
+
+		if (!strcmp (nm_device_get_iface (candidate), iface)) {
+			nm_device_handle_autoip4_event (candidate, event, address);
+			handled = TRUE;
+			break;
+		}
+	}
+
+	if (!handled)
+		nm_log_warn (LOGD_AUTOIP4, "(%s): unhandled avahi-autoipd event", iface);
+}
+
+static const char *
+hostname_provider_get_hostname (NMHostnameProvider *provider)
+{
+	return NM_MANAGER_GET_PRIVATE (provider)->hostname;
+}
+
+static void
+hostname_provider_init (NMHostnameProvider *provider_class)
+{
+	provider_class->get_hostname = hostname_provider_get_hostname;
+}
+
+NMState
+nm_manager_get_state (NMManager *manager)
+{
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NM_STATE_UNKNOWN);
+
+	return NM_MANAGER_GET_PRIVATE (manager)->state;
+}
+
+static void
+emit_removed (gpointer key, gpointer value, gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+	NMConnection *connection = NM_CONNECTION (value);
+
+	g_signal_emit (manager, signals[CONNECTION_REMOVED], 0,
+	               connection,
+	               nm_connection_get_scope (connection));
+}
+
+static void
+pending_connection_info_destroy (PendingConnectionInfo *info)
+{
+	if (!info)
+		return;
+
+	if (info->timeout_id)
+		g_source_remove (info->timeout_id);
+
+	g_free (info->connection_path);
+	g_free (info->specific_object_path);
+	g_free (info->device_path);
+
+	g_slice_free (PendingConnectionInfo, info);
+}
+
+static GPtrArray *
+get_active_connections (NMManager *manager, NMConnection *filter)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMVPNManager *vpn_manager;
+	GPtrArray *active;
+	GSList *iter;
+
+ 	active = g_ptr_array_sized_new (3);
+
+	/* Add active device connections */
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMActRequest *req;
+		const char *path;
+
+		req = nm_device_get_act_request (NM_DEVICE (iter->data));
+		if (!req)
+			continue;
+
+		if (!filter || (nm_act_request_get_connection (req) == filter)) {
+			path = nm_act_request_get_active_connection_path (req);
+			g_ptr_array_add (active, g_strdup (path));
+		}
+	}
+
+	/* Add active VPN connections */
+	vpn_manager = nm_vpn_manager_get ();
+	nm_vpn_manager_add_active_connections (vpn_manager, filter, active);
+	g_object_unref (vpn_manager);
+
+	return active;
+}
+
+static void
+remove_connection (NMManager *manager,
+                   NMConnection *connection,
+                   GHashTable *hash)
+{
+	/* Destroys the connection, then associated DBusGProxy due to the
+	 * weak reference notify function placed on the connection when it
+	 * was created.
+	 */
+	g_object_ref (connection);
+	g_hash_table_remove (hash, nm_connection_get_path (connection));
+	g_signal_emit (manager, signals[CONNECTION_REMOVED], 0,
+	               connection,
+	               nm_connection_get_scope (connection));
+	g_object_unref (connection);
+
+	bluez_manager_resync_devices (manager);
+}
+
+/*******************************************************************/
+/* User settings stuff via D-Bus                                   */
+/*******************************************************************/
+
+static void
+user_destroy_connections (NMManager *manager)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+
+	if (priv->user_connections) {
+		g_hash_table_foreach (priv->user_connections, emit_removed, manager);
+		g_hash_table_remove_all (priv->user_connections);
+	}
+
+	if (priv->user_proxy) {
+		g_object_unref (priv->user_proxy);
+		priv->user_proxy = NULL;
+	}
+}
+
+typedef struct GetSettingsInfo {
+	NMManager *manager;
+	NMConnection *connection;
+	DBusGProxy *proxy;
+	DBusGProxyCall *call;
+	GSList **calls;
+} GetSettingsInfo;
+
+static void
+free_get_settings_info (gpointer data)
+{
+	GetSettingsInfo *info = (GetSettingsInfo *) data;
+
+	/* If this was the last pending call for a batch of GetSettings calls,
+	 * send out the connections-added signal.
+	 */
+	if (info->calls) {
+		*(info->calls) = g_slist_remove (*(info->calls), info->call);
+		if (g_slist_length (*(info->calls)) == 0) {
+			g_slist_free (*(info->calls));
+			g_slice_free (GSList, (gpointer) info->calls);
+			g_signal_emit (info->manager, signals[CONNECTIONS_ADDED], 0, NM_CONNECTION_SCOPE_USER);
+
+			/* Update the Bluetooth connections for all the new connections */
+			bluez_manager_resync_devices (info->manager);
+		}
+	}
+
+	if (info->manager) {
+		g_object_unref (info->manager);
+		info->manager = NULL;
+	}
+	if (info->connection) {
+		g_object_unref (info->connection);
+		info->connection = NULL;
+	}
+	if (info->proxy) {
+		g_object_unref (info->proxy);
+		info->proxy = NULL;
+	}
+
+	g_slice_free (GetSettingsInfo, data);	
+}
+
+static void
+user_connection_get_settings_cb  (DBusGProxy *proxy,
+                                  DBusGProxyCall *call_id,
+                                  gpointer user_data)
+{
+	GetSettingsInfo *info = (GetSettingsInfo *) user_data;
+	GError *err = NULL;
+	GHashTable *settings = NULL;
+	NMConnection *connection;
+	NMManager *manager;
+
+	g_return_if_fail (info != NULL);
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err,
+	                            DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings,
+	                            G_TYPE_INVALID)) {
+		nm_log_info (LOGD_USER_SET, "couldn't retrieve connection settings: %s.",
+		             err && err->message ? err->message : "(unknown)");
+		g_error_free (err);
+		goto out;
+	}
+
+	manager = info->manager;
+	connection = info->connection;
+ 	if (connection == NULL) {
+		const char *path = dbus_g_proxy_get_path (proxy);
+		NMManagerPrivate *priv;
+		GError *error = NULL;
+		NMConnection *existing = NULL;
+
+		connection = nm_connection_new_from_hash (settings, &error);
+		if (connection == NULL) {
+			nm_log_warn (LOGD_USER_SET, "invalid connection: '%s' / '%s' invalid: %d",
+			             g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)),
+			             error->message, error->code);
+			g_error_free (error);
+			goto out;
+		}
+
+		nm_connection_set_path (connection, path);
+		nm_connection_set_scope (connection, NM_CONNECTION_SCOPE_USER);
+
+		/* Add the new connection to the internal hashes only if the same
+		 * connection isn't already there.
+		 */
+		priv = NM_MANAGER_GET_PRIVATE (manager);
+
+		existing = g_hash_table_lookup (priv->user_connections, path);
+		if (!existing || !nm_connection_compare (existing, connection, NM_SETTING_COMPARE_FLAG_EXACT)) {
+			g_hash_table_insert (priv->user_connections,
+			                     g_strdup (path),
+			                     connection);
+			existing = NULL;
+
+			/* Attach the D-Bus proxy representing the remote NMConnection
+			 * to the local NMConnection object to ensure it stays alive to
+			 * continue delivering signals.  It'll be destroyed once the
+			 * NMConnection is destroyed.
+			 */
+			g_object_set_data_full (G_OBJECT (connection),
+			                        "proxy",
+			                        g_object_ref (info->proxy),
+									g_object_unref);
+		} else
+			g_object_unref (connection);
+
+		/* If the connection-added signal is supposed to be batched, don't
+		 * emit the single connection-added here.  Also, don't emit the signal
+		 * if the connection wasn't actually added to the system or user hashes.
+		 */
+		if (!info->calls && !existing) {
+			g_signal_emit (manager, signals[CONNECTION_ADDED], 0, connection, NM_CONNECTION_SCOPE_USER);
+			/* Update the Bluetooth connections for that single new connection */
+			bluez_manager_resync_devices (manager);
+		}
+	} else {
+		// FIXME: merge settings? or just replace?
+		nm_log_dbg (LOGD_USER_SET, "implement merge settings");
+	}
+
+out:
+	if (settings)
+		g_hash_table_destroy (settings);
+
+	return;
+}
+
+static void
+user_connection_removed_cb (DBusGProxy *proxy, gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMConnection *connection = NULL;
+	const char *path;
+
+	path = dbus_g_proxy_get_path (proxy);
+	if (path) {
+		connection = g_hash_table_lookup (priv->user_connections, path);
+		if (connection)
+			remove_connection (manager, connection, priv->user_connections);
+	}
+}
+
+static void
+user_connection_updated_cb (DBusGProxy *proxy,
+                            GHashTable *settings,
+                            gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMConnection *new_connection;
+	NMConnection *old_connection = NULL;
+	gboolean valid = FALSE;
+	GError *error = NULL;
+	const char *path;
+
+	path = dbus_g_proxy_get_path (proxy);
+	if (path)
+		old_connection = g_hash_table_lookup (priv->user_connections, path);
+
+	g_return_if_fail (old_connection != NULL);
+
+	new_connection = nm_connection_new_from_hash (settings, &error);
+	if (!new_connection) {
+		/* New connection invalid, remove existing connection */
+		nm_log_warn (LOGD_USER_SET, "invalid connection: '%s' / '%s' invalid: %d",
+		             g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)),
+		             error->message, error->code);
+		g_error_free (error);
+		remove_connection (manager, old_connection, priv->user_connections);
+		return;
+	}
+	g_object_unref (new_connection);
+
+	valid = nm_connection_replace_settings (old_connection, settings, NULL);
+	if (valid) {
+		g_signal_emit (manager, signals[CONNECTION_UPDATED], 0,
+		               old_connection,
+		               nm_connection_get_scope (old_connection));
+
+		bluez_manager_resync_devices (manager);
+	} else {
+		remove_connection (manager, old_connection, priv->user_connections);
+	}
+}
+
+static void
+user_internal_new_connection_cb (DBusGProxy *proxy,
+                                 const char *path,
+                                 NMManager *manager,
+                                 GSList **calls)
+{
+	struct GetSettingsInfo *info;
+	DBusGProxy *con_proxy;
+	DBusGConnection *g_connection;
+	DBusGProxyCall *call;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+
+	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	con_proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                       dbus_g_proxy_get_bus_name (proxy),
+	                                       path,
+	                                       NM_DBUS_IFACE_SETTINGS_CONNECTION);
+	if (!con_proxy) {
+		nm_log_err (LOGD_USER_SET, "could not init user connection proxy");
+		return;
+	}
+
+	dbus_g_proxy_add_signal (con_proxy, "Updated",
+	                         DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT,
+	                         G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (con_proxy, "Updated",
+	                             G_CALLBACK (user_connection_updated_cb),
+	                             manager,
+	                             NULL);
+
+	dbus_g_proxy_add_signal (con_proxy, "Removed", G_TYPE_INVALID, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (con_proxy, "Removed",
+	                             G_CALLBACK (user_connection_removed_cb),
+	                             manager,
+	                             NULL);
+
+	info = g_slice_new0 (GetSettingsInfo);
+	info->manager = g_object_ref (manager);
+	info->calls = calls;
+	call = dbus_g_proxy_begin_call (con_proxy, "GetSettings",
+	                                user_connection_get_settings_cb,
+	                                info,
+	                                free_get_settings_info,
+	                                G_TYPE_INVALID);
+	info->call = call;
+	info->proxy = con_proxy;
+	if (info->calls)
+		*(info->calls) = g_slist_prepend (*(info->calls), call);
+}
+
+static void
+user_list_connections_cb  (DBusGProxy *proxy,
+                           DBusGProxyCall *call_id,
+                           gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+	GError *err = NULL;
+	GPtrArray *ops;
+	GSList **calls = NULL;
+	int i;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err,
+	                            DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &ops,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_USER_SET, "couldn't retrieve connections: %s",
+		             err && err->message ? err->message : "(unknown)");
+		g_error_free (err);
+		goto out;
+	}
+
+	/* Keep track of all calls made here; don't want to emit connection-added for
+	 * each one, but emit connections-added when they are all done.
+	 */
+	calls = g_slice_new0 (GSList *);
+
+	for (i = 0; i < ops->len; i++) {
+		char *op = g_ptr_array_index (ops, i);
+
+		user_internal_new_connection_cb (proxy, op, manager, calls);
+		g_free (op);
+	}
+
+	g_ptr_array_free (ops, TRUE);
+
+out:
+	return;
+}
+
+static void
+user_new_connection_cb (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+	user_internal_new_connection_cb (proxy, path, NM_MANAGER (user_data), NULL);
+}
+
+static void
+user_query_connections (NMManager *manager)
+{
+	NMManagerPrivate *priv;
+	DBusGProxyCall *call;
+	DBusGConnection *g_connection;
+
+	g_return_if_fail (NM_IS_MANAGER (manager));
+
+	priv = NM_MANAGER_GET_PRIVATE (manager);
+	if (!priv->user_proxy) {
+		g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+		priv->user_proxy = dbus_g_proxy_new_for_name (g_connection,
+		                                              NM_DBUS_SERVICE_USER_SETTINGS,
+		                                              NM_DBUS_PATH_SETTINGS,
+		                                              NM_DBUS_IFACE_SETTINGS);
+		if (!priv->user_proxy) {
+			nm_log_err (LOGD_USER_SET, "could not init user settings proxy");
+			return;
+		}
+
+		dbus_g_proxy_add_signal (priv->user_proxy,
+		                         "NewConnection",
+		                         DBUS_TYPE_G_OBJECT_PATH,
+		                         G_TYPE_INVALID);
+
+		dbus_g_proxy_connect_signal (priv->user_proxy, "NewConnection",
+		                             G_CALLBACK (user_new_connection_cb),
+		                             manager,
+		                             NULL);
+	}
+
+	/* grab connections */
+	call = dbus_g_proxy_begin_call (priv->user_proxy, "ListConnections",
+	                                user_list_connections_cb,
+	                                manager,
+	                                NULL,
+	                                G_TYPE_INVALID);
+}
+
+/*******************************************************************/
+/* System settings stuff via NMSysconfigSettings                   */
+/*******************************************************************/
+
+static void
+system_connection_updated_cb (NMSettingsConnectionInterface *connection,
+                              gpointer unused,
+                              NMManager *manager)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	const char *path;
+	NMSettingsConnectionInterface *existing;
+	GError *error = NULL;
+
+	path = nm_connection_get_path (NM_CONNECTION (connection));
+
+	existing = g_hash_table_lookup (priv->system_connections, path);
+	if (!existing)
+		return;
+	if (existing != connection) {
+		nm_log_warn (LOGD_SYS_SET, "existing connection didn't matched updated.");
+		return;
+	}
+
+	if (!nm_connection_verify (NM_CONNECTION (existing), &error)) {
+		/* Updated connection invalid, remove existing connection */
+		nm_log_warn (LOGD_SYS_SET, "invalid connection: '%s' / '%s' invalid: %d",
+		             g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)),
+		             error->message, error->code);
+		g_error_free (error);
+		remove_connection (manager, NM_CONNECTION (existing), priv->system_connections);
+		return;
+	}
+
+	g_signal_emit (manager, signals[CONNECTION_UPDATED], 0,
+	               existing, NM_CONNECTION_SCOPE_SYSTEM);
+
+	bluez_manager_resync_devices (manager);
+}
+
+static void
+system_connection_removed_cb (NMSettingsConnectionInterface *connection,
+                              NMManager *manager)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	const char *path;
+
+	path = nm_connection_get_path (NM_CONNECTION (connection));
+
+	connection = g_hash_table_lookup (priv->system_connections, path);
+	if (connection)
+		remove_connection (manager, NM_CONNECTION (connection), priv->system_connections);
+}
+
+static void
+system_internal_new_connection (NMManager *manager,
+                                NMSettingsConnectionInterface *connection)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	const char *path;
+
+	g_return_if_fail (connection != NULL);
+
+	g_signal_connect (connection, NM_SETTINGS_CONNECTION_INTERFACE_UPDATED,
+	                  G_CALLBACK (system_connection_updated_cb), manager);
+	g_signal_connect (connection, NM_SETTINGS_CONNECTION_INTERFACE_REMOVED,
+	                  G_CALLBACK (system_connection_removed_cb), manager);
+
+	path = nm_connection_get_path (NM_CONNECTION (connection));
+	g_hash_table_insert (priv->system_connections, g_strdup (path),
+	                     g_object_ref (connection));
+}
+
+static void
+system_new_connection_cb (NMSysconfigSettings *settings,
+                          NMSettingsConnectionInterface *connection,
+                          NMManager *manager)
+{
+	system_internal_new_connection (manager, connection);
+}
+
+static void
+system_query_connections (NMManager *manager)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	GSList *system_connections, *iter;
+
+	system_connections = nm_settings_interface_list_connections (NM_SETTINGS_INTERFACE (priv->sys_settings));
+	for (iter = system_connections; iter; iter = g_slist_next (iter))
+		system_internal_new_connection (manager, NM_SETTINGS_CONNECTION_INTERFACE (iter->data));
+	g_slist_free (system_connections);
+}
+
+static void
+system_unmanaged_devices_changed_cb (NMSysconfigSettings *sys_settings,
+                                     GParamSpec *pspec,
+                                     gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	const GSList *unmanaged_specs, *iter;
+
+	unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (sys_settings);
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *device = NM_DEVICE (iter->data);
+		gboolean managed;
+
+		managed = !nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs);
+		nm_device_set_managed (device,
+		                       managed,
+		                       managed ? NM_DEVICE_STATE_REASON_NOW_MANAGED :
+		                                   NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
+	}
+}
+
+static void
+system_hostname_changed_cb (NMSysconfigSettings *sys_settings,
+                            GParamSpec *pspec,
+                            gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	char *hostname;
+
+	hostname = nm_sysconfig_settings_get_hostname (sys_settings);
+
+	if (!hostname && !priv->hostname)
+		return;
+
+	if (hostname && priv->hostname && !strcmp (hostname, priv->hostname))
+		return;
+
+	g_free (priv->hostname);
+	priv->hostname = (hostname && strlen (hostname)) ? g_strdup (hostname) : NULL;
+	g_object_notify (G_OBJECT (manager), NM_MANAGER_HOSTNAME);
+
+	g_free (hostname);
+}
+
+/*******************************************************************/
+/* General NMManager stuff                                         */
+/*******************************************************************/
+
+static NMDevice *
+nm_manager_get_device_by_udi (NMManager *manager, const char *udi)
+{
+	GSList *iter;
+
+	for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
+		if (!strcmp (nm_device_get_udi (NM_DEVICE (iter->data)), udi))
+			return NM_DEVICE (iter->data);
+	}
+	return NULL;
+}
+
+static NMDevice *
+nm_manager_get_device_by_path (NMManager *manager, const char *path)
+{
+	GSList *iter;
+
+	for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
+		if (!strcmp (nm_device_get_path (NM_DEVICE (iter->data)), path))
+			return NM_DEVICE (iter->data);
+	}
+	return NULL;
+}
+
+static void
+nm_manager_name_owner_changed (NMDBusManager *mgr,
+                               const char *name,
+                               const char *old,
+                               const char *new,
+                               gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+	gboolean old_owner_good = (old && (strlen (old) > 0));
+	gboolean new_owner_good = (new && (strlen (new) > 0));
+
+	if (strcmp (name, NM_DBUS_SERVICE_USER_SETTINGS) == 0) {
+		if (!old_owner_good && new_owner_good) {
+			/* User Settings service appeared, update stuff */
+			user_query_connections (manager);
+		} else {
+			/* User Settings service disappeared, throw them away (?) */
+			user_destroy_connections (manager);
+			bluez_manager_resync_devices (manager);
+		}
+	}
+}
+
+/* Store value into key-file; supported types: boolean, int, string */
+static gboolean
+write_value_to_state_file (const char *filename,
+                           const char *group,
+                           const char *key,
+                           GType value_type,
+                           gpointer value,
+                           GError **error)
+{
+	GKeyFile *key_file;
+	char *data;
+	gsize len = 0;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail (filename != NULL, FALSE);
+	g_return_val_if_fail (group != NULL, FALSE);
+	g_return_val_if_fail (key != NULL, FALSE);
+	g_return_val_if_fail (value_type == G_TYPE_BOOLEAN ||
+	                      value_type == G_TYPE_INT ||
+	                      value_type == G_TYPE_STRING,
+	                      FALSE);
+
+	key_file = g_key_file_new ();
+	if (!key_file)
+		return FALSE;
+
+	g_key_file_set_list_separator (key_file, ',');
+	g_key_file_load_from_file (key_file, filename, G_KEY_FILE_KEEP_COMMENTS, NULL);
+	switch (value_type) {
+	case G_TYPE_BOOLEAN:
+		g_key_file_set_boolean (key_file, group, key, *((gboolean *) value));
+		break;
+	case G_TYPE_INT:
+		g_key_file_set_integer (key_file, group, key, *((gint *) value));
+		break;
+	case G_TYPE_STRING:
+		g_key_file_set_string (key_file, group, key, *((const gchar **) value));
+		break;
+	}
+
+	data = g_key_file_to_data (key_file, &len, NULL);
+	if (data) {
+		ret = g_file_set_contents (filename, data, len, error);
+		g_free (data);
+	}
+	g_key_file_free (key_file);
+
+	return ret;
+}
+
+static void
+manager_set_radio_enabled (NMManager *manager,
+                           RadioState *rstate,
+                           gboolean enabled)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	GSList *iter;
+	GError *error = NULL;
+
+	/* Do nothing for radio types not yet implemented */
+	if (!rstate->prop)
+		return;
+
+	if (rstate->enabled == enabled)
+		return;
+
+	/* Can't set wireless enabled if it's disabled in hardware */
+	if (!rstate->hw_enabled && enabled)
+		return;
+
+	rstate->enabled = enabled;
+
+	g_object_notify (G_OBJECT (manager), rstate->prop);
+
+	/* Update enabled key in state file */
+	if (priv->state_file) {
+		if (!write_value_to_state_file (priv->state_file,
+		                                "main", rstate->key,
+		                                G_TYPE_BOOLEAN, (gpointer) &enabled,
+		                                &error)) {
+			nm_log_warn (LOGD_CORE, "writing to state file %s failed: (%d) %s.",
+			             priv->state_file,
+			             error ? error->code : -1,
+			             (error && error->message) ? error->message : "unknown");
+		}
+	}
+
+	/* Don't touch devices if asleep/networking disabled */
+	if (manager_sleeping (manager))
+		return;
+
+	/* enable/disable wireless devices as required */
+	for (iter = priv->devices; iter; iter = iter->next) {
+		RfKillType devtype = RFKILL_TYPE_UNKNOWN;
+
+		g_object_get (G_OBJECT (iter->data), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL);
+		if (devtype == rstate->rtype) {
+			nm_log_dbg (LOGD_RFKILL, "(%s): setting radio %s",
+			            nm_device_get_iface (NM_DEVICE (iter->data)),
+			            enabled ? "enabled" : "disabled");
+			nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (iter->data), enabled);
+		}
+	}
+}
+
+static void
+manager_hidden_ap_found (NMDeviceInterface *device,
+                         NMAccessPoint *ap,
+                         gpointer user_data)
+{
+	NMManager *manager = NM_MANAGER (user_data);
+	const struct ether_addr *ap_addr;
+	const GByteArray *ap_ssid;
+	GSList *iter;
+	GSList *connections;
+	gboolean done = FALSE;
+
+	ap_ssid = nm_ap_get_ssid (ap);
+	if (ap_ssid && ap_ssid->len)
+		return;
+
+	ap_addr = nm_ap_get_address (ap);
+	g_assert (ap_addr);
+
+	/* Look for this AP's BSSID in the seen-bssids list of a connection,
+	 * and if a match is found, copy over the SSID */
+	connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM);
+	connections = g_slist_concat (connections,  nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER));
+
+	for (iter = connections; iter && !done; iter = g_slist_next (iter)) {
+		NMConnection *connection = NM_CONNECTION (iter->data);
+		NMSettingWireless *s_wireless;
+		const GByteArray *ssid;
+		guint32 num_bssids;
+		guint32 i;
+
+		s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+		if (!s_wireless)
+			goto next;
+
+		num_bssids = nm_setting_wireless_get_num_seen_bssids (s_wireless);
+		if (num_bssids < 1)
+			goto next;
+
+		ssid = nm_setting_wireless_get_ssid (s_wireless);
+		g_assert (ssid);
+
+		for (i = 0; i < num_bssids; i++) {
+			const char *seen_bssid = nm_setting_wireless_get_seen_bssid (s_wireless, i);
+			struct ether_addr seen_addr;
+
+			if (!ether_aton_r (seen_bssid, &seen_addr))
+				continue;
+
+			if (memcmp (ap_addr, &seen_addr, sizeof (struct ether_addr)))
+				continue;
+
+			/* Copy the SSID from the connection to the AP */
+			nm_ap_set_ssid (ap, ssid);
+			done = TRUE;
+		}
+
+next:
+		g_object_unref (connection);
+	}
+	g_slist_free (connections);
+}
+
+static RfKillState
+nm_manager_get_ipw_rfkill_state (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+	RfKillState ipw_state = RFKILL_UNBLOCKED;
+
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *candidate = NM_DEVICE (iter->data);
+		RfKillState candidate_state;
+
+		if (NM_IS_DEVICE_WIFI (candidate)) {
+			candidate_state = nm_device_wifi_get_ipw_rfkill_state (NM_DEVICE_WIFI (candidate));
+
+			if (candidate_state > ipw_state)
+				ipw_state = candidate_state;
+		}
+	}
+
+	return ipw_state;
+}
+
+static RfKillState
+nm_manager_get_modem_enabled_state (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+	RfKillState wwan_state = RFKILL_UNBLOCKED;
+
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *candidate = NM_DEVICE (iter->data);
+		RfKillState candidate_state = RFKILL_UNBLOCKED;
+		RfKillType devtype = RFKILL_TYPE_UNKNOWN;
+
+		g_object_get (G_OBJECT (candidate), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL);
+		if (devtype == RFKILL_TYPE_WWAN) {
+			if (!nm_device_interface_get_enabled (NM_DEVICE_INTERFACE (candidate)))
+				candidate_state = RFKILL_SOFT_BLOCKED;
+
+			if (candidate_state > wwan_state)
+				wwan_state = candidate_state;
+		}
+	}
+
+	return wwan_state;
+}
+
+static void
+manager_rfkill_update_one_type (NMManager *self,
+                                RadioState *rstate,
+                                RfKillType rtype)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	RfKillState udev_state = RFKILL_UNBLOCKED;
+	RfKillState other_state = RFKILL_UNBLOCKED;
+	RfKillState composite;
+	gboolean new_e = TRUE, new_he = TRUE;
+
+	udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, rtype);
+
+	if (rstate->other_enabled_func)
+		other_state = rstate->other_enabled_func (self);
+
+	/* The composite state is the "worst" of either udev or other states */
+	if (udev_state == RFKILL_HARD_BLOCKED || other_state == RFKILL_HARD_BLOCKED)
+		composite = RFKILL_HARD_BLOCKED;
+	else if (udev_state == RFKILL_SOFT_BLOCKED || other_state == RFKILL_SOFT_BLOCKED)
+		composite = RFKILL_SOFT_BLOCKED;
+	else
+		composite = RFKILL_UNBLOCKED;
+
+	switch (composite) {
+	case RFKILL_UNBLOCKED:
+		new_e = TRUE;
+		new_he = TRUE;
+		break;
+	case RFKILL_SOFT_BLOCKED:
+		new_e = FALSE;
+		new_he = TRUE;
+		break;
+	case RFKILL_HARD_BLOCKED:
+		new_e = FALSE;
+		new_he = FALSE;
+		break;
+	default:
+		break;
+	}
+
+	if (rstate->desc) {
+		nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d enabled %d",
+		            rstate->desc, new_he, new_e);
+	}
+
+	if (new_he != rstate->hw_enabled) {
+		nm_log_info (LOGD_RFKILL, "%s now %s by radio killswitch",
+		             rstate->desc,
+		             (new_e && new_he) ? "enabled" : "disabled");
+
+		rstate->hw_enabled = new_he;
+		if (rstate->hw_prop)
+			g_object_notify (G_OBJECT (self), rstate->hw_prop);
+	}
+	manager_set_radio_enabled (self, rstate, new_e);
+}
+
+static void
+nm_manager_rfkill_update (NMManager *self, RfKillType rtype)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	guint i;
+
+	if (rtype != RFKILL_TYPE_UNKNOWN) {
+		manager_rfkill_update_one_type (self, &priv->radio_states[rtype], rtype);
+		return;
+	}
+
+	/* Otherwise sync all radio types */
+	for (i = 0; i < RFKILL_TYPE_MAX; i++)
+		manager_rfkill_update_one_type (self, &priv->radio_states[i], i);
+}
+
+static void
+manager_ipw_rfkill_state_changed (NMDeviceWifi *device,
+                                  GParamSpec *pspec,
+                                  gpointer user_data)
+{
+	nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WLAN);
+}
+
+static void
+manager_modem_enabled_changed (NMModem *device, gpointer user_data)
+{
+	nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WWAN);
+}
+
+static void
+add_device (NMManager *self, NMDevice *device)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	const char *iface, *driver, *type_desc;
+	char *path;
+	static guint32 devcount = 0;
+	const GSList *unmanaged_specs;
+	NMConnection *existing = NULL;
+	GHashTableIter iter;
+	gpointer value;
+	gboolean managed = FALSE;
+
+	iface = nm_device_get_ip_iface (device);
+	g_assert (iface);
+
+	if (!NM_IS_DEVICE_MODEM (device) && find_device_by_iface (self, iface)) {
+		g_object_unref (device);
+		return;
+	}
+
+	priv->devices = g_slist_append (priv->devices, device);
+
+	g_signal_connect (device, "state-changed",
+					  G_CALLBACK (manager_device_state_changed),
+					  self);
+
+	if (NM_IS_DEVICE_WIFI (device)) {
+		/* Attach to the access-point-added signal so that the manager can fill
+		 * non-SSID-broadcasting APs with an SSID.
+		 */
+		g_signal_connect (device, "hidden-ap-found",
+						  G_CALLBACK (manager_hidden_ap_found),
+						  self);
+
+		/* Hook up rfkill handling for ipw-based cards until they get converted
+		 * to use the kernel's rfkill subsystem in 2.6.33.
+		 */
+		g_signal_connect (device, "notify::" NM_DEVICE_WIFI_IPW_RFKILL_STATE,
+		                  G_CALLBACK (manager_ipw_rfkill_state_changed),
+		                  self);
+
+		/* Update global rfkill state with this device's rfkill state, and
+		 * then set this device's rfkill state based on the global state.
+		 */
+		nm_manager_rfkill_update (self, RFKILL_TYPE_WLAN);
+		nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device),
+		                                 priv->radio_states[RFKILL_TYPE_WLAN].enabled);
+	} else if (NM_IS_DEVICE_MODEM (device)) {
+		g_signal_connect (device, NM_DEVICE_MODEM_ENABLE_CHANGED,
+		                  G_CALLBACK (manager_modem_enabled_changed),
+		                  self);
+
+		nm_manager_rfkill_update (self, RFKILL_TYPE_WWAN);
+		/* Until we start respecting WWAN rfkill switches the modem itself
+		 * is the source of the enabled/disabled state, so the manager shouldn't
+		 * touch it here.
+		nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device),
+		                                 priv->radio_states[RFKILL_TYPE_WWAN].enabled);
+		*/
+	}
+
+	type_desc = nm_device_get_type_desc (device);
+	g_assert (type_desc);
+	driver = nm_device_get_driver (device);
+	if (!driver)
+		driver = "unknown";
+	nm_log_info (LOGD_HW, "(%s): new %s device (driver: '%s' ifindex: %d)",
+	             iface, type_desc, driver, nm_device_get_ifindex (device));
+
+	path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++);
+	nm_device_set_path (device, path);
+	dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (priv->dbus_mgr),
+	                                     path,
+	                                     G_OBJECT (device));
+	nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path);
+	g_free (path);
+
+	/* Check if we should assume the device's active connection by matching its
+	 * config with an existing system connection.
+	 */
+	if (nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device))) {
+		GSList *connections = NULL;
+
+		g_hash_table_iter_init (&iter, priv->system_connections);
+		while (g_hash_table_iter_next (&iter, NULL, &value))
+			connections = g_slist_append (connections, value);
+		existing = nm_device_interface_connection_match_config (NM_DEVICE_INTERFACE (device),
+		                                                        (const GSList *) connections);
+		g_slist_free (connections);
+
+		if (existing) {
+			NMSettingConnection *s_con;
+
+			s_con = (NMSettingConnection *) nm_connection_get_setting (existing, NM_TYPE_SETTING_CONNECTION);
+			nm_log_dbg (LOGD_DEVICE, "(%s): found existing device connection '%s'",
+			            nm_device_get_iface (device),
+			            nm_setting_connection_get_id (s_con));
+		}
+	}
+
+	/* Start the device if it's supposed to be managed */
+	unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings);
+	if (   !manager_sleeping (self)
+	    && !nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs)) {
+		nm_device_set_managed (device,
+		                       TRUE,
+		                       existing ? NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED :
+		                                  NM_DEVICE_STATE_REASON_NOW_MANAGED);
+		managed = TRUE;
+	}
+
+	nm_sysconfig_settings_device_added (priv->sys_settings, device);
+	g_signal_emit (self, signals[DEVICE_ADDED], 0, device);
+
+	/* If the device has a connection it can assume, do that now */
+	if (existing && managed && nm_device_is_available (device)) {
+		const char *ac_path;
+		GError *error = NULL;
+
+		nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume existing connection",
+		            nm_device_get_iface (device));
+
+		ac_path = internal_activate_device (self, device, existing, NULL, FALSE, TRUE, &error);
+		if (ac_path)
+			g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS);
+		else {
+			nm_log_warn (LOGD_DEVICE, "assumed connection (%d) %s failed to activate: (%d) %s",
+			             nm_connection_get_scope (existing),
+			             nm_connection_get_path (existing),
+			             error ? error->code : -1,
+			             error && error->message ? error->message : "(unknown)");
+			g_error_free (error);
+		}
+	}
+}
+
+static gboolean
+bdaddr_matches_connection (NMSettingBluetooth *s_bt, const char *bdaddr)
+{
+	const GByteArray *arr;
+	gboolean ret = FALSE;
+
+	arr = nm_setting_bluetooth_get_bdaddr (s_bt);
+
+	if (   arr != NULL 
+	       && arr->len == ETH_ALEN) {
+		char *str;
+
+		str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+				       arr->data[0],
+				       arr->data[1],
+				       arr->data[2],
+				       arr->data[3],
+				       arr->data[4],
+				       arr->data[5]);
+		ret = g_str_equal (str, bdaddr);
+		g_free (str);
+	}
+
+	return ret;
+}
+
+static NMConnection *
+bluez_manager_find_connection (NMManager *manager,
+                               const char *bdaddr,
+                               guint32 capabilities)
+{
+	NMConnection *found = NULL;
+	GSList *connections, *l;
+
+	connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM);
+	connections = g_slist_concat (connections, nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER));
+
+	for (l = connections; l != NULL; l = l->next) {
+		NMConnection *candidate = NM_CONNECTION (l->data);
+		NMSettingConnection *s_con;
+		NMSettingBluetooth *s_bt;
+		const char *con_type;
+		const char *bt_type;
+
+		s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (candidate, NM_TYPE_SETTING_CONNECTION));
+		g_assert (s_con);
+		con_type = nm_setting_connection_get_connection_type (s_con);
+		g_assert (con_type);
+		if (!g_str_equal (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME))
+			continue;
+
+		s_bt = (NMSettingBluetooth *) nm_connection_get_setting (candidate, NM_TYPE_SETTING_BLUETOOTH);
+		if (!s_bt)
+			continue;
+
+		if (!bdaddr_matches_connection (s_bt, bdaddr))
+			continue;
+
+		bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
+		if (   g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
+		    && !(capabilities & NM_BT_CAPABILITY_DUN))
+		    	continue;
+		if (   g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)
+		    && !(capabilities & NM_BT_CAPABILITY_NAP))
+		    	continue;
+
+		found = candidate;
+		break;
+	}
+
+	g_slist_free (connections);
+	return found;
+}
+
+static void
+bluez_manager_resync_devices (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GSList *iter, *gone = NULL, *keep = NULL;
+
+	/* Remove devices from the device list that don't have a corresponding connection */
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *candidate = NM_DEVICE (iter->data);
+		guint32 uuids;
+		const char *bdaddr;
+
+		if (NM_IS_DEVICE_BT (candidate)) {
+			uuids = nm_device_bt_get_capabilities (NM_DEVICE_BT (candidate));
+			bdaddr = nm_device_bt_get_hw_address (NM_DEVICE_BT (candidate));
+
+			if (bluez_manager_find_connection (self, bdaddr, uuids))
+				keep = g_slist_prepend (keep, candidate);
+			else
+				gone = g_slist_prepend (gone, candidate);
+		} else
+			keep = g_slist_prepend (keep, candidate);
+	}
+
+	/* Only touch the device list if anything actually changed */
+	if (g_slist_length (gone)) {
+		g_slist_free (priv->devices);
+		priv->devices = keep;
+
+		while (g_slist_length (gone))
+			gone = remove_one_device (self, gone, NM_DEVICE (gone->data), FALSE, TRUE);
+	} else {
+		g_slist_free (keep);
+		g_slist_free (gone);
+	}
+
+	/* Now look for devices without connections */
+	nm_bluez_manager_query_devices (priv->bluez_mgr);
+}
+
+static void
+bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr,
+                               const char *bdaddr,
+                               const char *name,
+                               const char *object_path,
+                               guint32 capabilities,
+                               NMManager *manager)
+{
+	NMDevice *device;
+	gboolean has_dun = (capabilities & NM_BT_CAPABILITY_DUN);
+	gboolean has_nap = (capabilities & NM_BT_CAPABILITY_NAP);
+
+	g_return_if_fail (bdaddr != NULL);
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (object_path != NULL);
+	g_return_if_fail (capabilities != NM_BT_CAPABILITY_NONE);
+
+	/* Make sure the device is not already in the device list */
+	if (nm_manager_get_device_by_udi (manager, object_path))
+		return;
+
+	if (has_dun == FALSE && has_nap == FALSE)
+		return;
+
+	if (!bluez_manager_find_connection (manager, bdaddr, capabilities))
+		return;
+
+	device = nm_device_bt_new (object_path, bdaddr, name, capabilities, FALSE);
+	if (device) {
+		nm_log_info (LOGD_HW, "BT device %s (%s) added (%s%s%s)",
+		             name,
+		             bdaddr,
+		             has_dun ? "DUN" : "",
+		             has_dun && has_nap ? " " : "",
+		             has_nap ? "NAP" : "");
+
+		add_device (manager, device);
+	}
+}
+
+static void
+bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr,
+                                 const char *bdaddr,
+                                 const char *object_path,
+                                 gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+
+	g_return_if_fail (bdaddr != NULL);
+	g_return_if_fail (object_path != NULL);
+
+	nm_log_info (LOGD_HW, "BT device %s removed", bdaddr);
+
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *device = NM_DEVICE (iter->data);
+
+		if (!strcmp (nm_device_get_udi (device), object_path)) {
+			priv->devices = remove_one_device (self, priv->devices, device, FALSE, TRUE);
+			break;
+		}
+	}
+}
+
+static NMDevice *
+find_device_by_iface (NMManager *self, const gchar *iface)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *device = NM_DEVICE (iter->data);
+		const gchar *d_iface = nm_device_get_ip_iface (device);
+		if (!strcmp (d_iface, iface))
+			return device;
+	}
+
+	return NULL;
+}
+
+static NMDevice *
+find_device_by_ifindex (NMManager *self, guint32 ifindex)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *candidate = NM_DEVICE (iter->data);
+
+		if (ifindex == nm_device_get_ifindex (candidate))
+			return candidate;
+	}
+	return NULL;
+}
+
+static void
+udev_device_added_cb (NMUdevManager *udev_mgr,
+                      GUdevDevice *udev_device,
+                      NMDeviceCreatorFn creator_fn,
+                      gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	GObject *device;
+	guint32 ifindex;
+
+	ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
+	if (find_device_by_ifindex (self, ifindex))
+		return;
+
+	device = creator_fn (udev_mgr, udev_device, manager_sleeping (self));
+	if (device)
+		add_device (self, NM_DEVICE (device));
+}
+
+static void
+udev_device_removed_cb (NMUdevManager *manager,
+                        GUdevDevice *udev_device,
+                        gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *device;
+	guint32 ifindex;
+
+	ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
+	device = find_device_by_ifindex (self, ifindex);
+	if (device)
+		priv->devices = remove_one_device (self, priv->devices, device, FALSE, TRUE);
+}
+
+static void
+udev_manager_rfkill_changed_cb (NMUdevManager *udev_mgr,
+                                RfKillType rtype,
+                                RfKillState udev_state,
+                                gpointer user_data)
+{
+	nm_manager_rfkill_update (NM_MANAGER (user_data), rtype);
+}
+
+GSList *
+nm_manager_get_devices (NMManager *manager)
+{
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+
+	return NM_MANAGER_GET_PRIVATE (manager)->devices;
+}
+
+static gboolean
+impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	GSList *iter;
+
+	*devices = g_ptr_array_sized_new (g_slist_length (priv->devices));
+
+	for (iter = priv->devices; iter; iter = iter->next)
+		g_ptr_array_add (*devices, g_strdup (nm_device_get_path (NM_DEVICE (iter->data))));
+
+	return TRUE;
+}
+
+static NMActRequest *
+nm_manager_get_act_request_by_path (NMManager *manager,
+                                    const char *path,
+                                    NMDevice **device)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	GSList *iter;
+
+	g_return_val_if_fail (manager != NULL, NULL);
+	g_return_val_if_fail (path != NULL, NULL);
+	g_return_val_if_fail (device != NULL, NULL);
+	g_return_val_if_fail (*device == NULL, NULL);
+
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMActRequest *req;
+		const char *ac_path;
+
+		req = nm_device_get_act_request (NM_DEVICE (iter->data));
+		if (!req)
+			continue;
+
+		ac_path = nm_act_request_get_active_connection_path (req);
+		if (!strcmp (path, ac_path)) {
+			*device = NM_DEVICE (iter->data);
+			return req;
+		}
+	}
+
+	return NULL;
+}
+
+typedef struct GetSecretsInfo {
+	NMManager *manager;
+	NMSecretsProviderInterface *provider;
+
+	char *setting_name;
+	RequestSecretsCaller caller;
+	gboolean request_new;
+
+	/* User connection bits */
+	DBusGProxy *proxy;
+	DBusGProxyCall *call;
+
+	/* System connection bits */
+	guint32 idle_id;
+	char *hint1;
+	char *hint2;
+	char *connection_path;
+} GetSecretsInfo;
+
+static void
+free_get_secrets_info (gpointer data)
+{
+	GetSecretsInfo *info = data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (info->manager);
+
+	g_object_weak_unref (G_OBJECT (info->provider), (GWeakNotify) free_get_secrets_info, info);
+
+	priv->secrets_calls = g_slist_remove (priv->secrets_calls, info);
+
+	if (info->proxy) {
+		if (info->call)
+			dbus_g_proxy_cancel_call (info->proxy, info->call);
+		g_object_unref (info->proxy);
+	}
+
+	if (info->idle_id)
+		g_source_remove (info->idle_id);
+
+	g_free (info->hint1);
+	g_free (info->hint2);
+	g_free (info->setting_name);
+	g_free (info->connection_path);
+	memset (info, 0, sizeof (GetSecretsInfo));
+	g_free (info);
+}
+
+static void
+provider_cancel_secrets (NMSecretsProviderInterface *provider, gpointer user_data)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (user_data);
+	GSList *iter;
+
+	for (iter = priv->secrets_calls; iter; iter = g_slist_next (iter)) {
+		GetSecretsInfo *candidate = iter->data;
+
+		if (candidate->provider == provider) {
+			free_get_secrets_info (candidate);
+			break;
+		}
+	}
+}
+
+static void
+user_get_secrets_cb (DBusGProxy *proxy,
+                     DBusGProxyCall *call,
+                     gpointer user_data)
+{
+	GetSecretsInfo *info = (GetSecretsInfo *) user_data;
+	GHashTable *settings = NULL;
+	GError *error = NULL;
+	GObject *provider;
+
+	g_return_if_fail (info != NULL);
+	g_return_if_fail (info->provider);
+	g_return_if_fail (info->setting_name);
+
+	provider = g_object_ref (info->provider);
+
+	if (dbus_g_proxy_end_call (proxy, call, &error,
+	                           DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings,
+	                           G_TYPE_INVALID)) {
+		nm_secrets_provider_interface_get_secrets_result (info->provider,
+		                                                  info->setting_name,
+		                                                  info->caller,
+		                                                  settings,
+		                                                  NULL);
+		g_hash_table_destroy (settings);
+	} else {
+		nm_secrets_provider_interface_get_secrets_result (info->provider,
+		                                                  info->setting_name,
+		                                                  info->caller,
+		                                                  NULL,
+		                                                  error);
+		g_clear_error (&error);
+	}
+
+	info->call = NULL;
+	free_get_secrets_info (info);
+
+	g_object_unref (provider);
+}
+
+static GetSecretsInfo *
+user_get_secrets (NMManager *self,
+                  NMSecretsProviderInterface *provider,
+                  NMConnection *connection,
+                  const char *setting_name,
+                  gboolean request_new,
+                  RequestSecretsCaller caller_id,
+                  const char *hint1,
+                  const char *hint2)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	DBusGConnection *g_connection;
+	GetSecretsInfo *info = NULL;
+	GPtrArray *hints = NULL;
+
+	info = g_malloc0 (sizeof (GetSecretsInfo));
+
+	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	info->proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                         NM_DBUS_SERVICE_USER_SETTINGS,
+	                                         nm_connection_get_path (connection),
+	                                         NM_DBUS_IFACE_SETTINGS_CONNECTION_SECRETS);
+	if (!info->proxy) {
+		nm_log_warn (LOGD_USER_SET, "could not create user connection secrets proxy");
+		g_free (info);
+		return NULL;
+	}
+
+	info->manager = self;
+	info->provider = provider;
+	info->caller = caller_id;
+	info->setting_name = g_strdup (setting_name);
+
+	g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) free_get_secrets_info, info);
+
+	hints = g_ptr_array_sized_new (2);
+	if (hint1)
+		g_ptr_array_add (hints, (char *) hint1);
+	if (hint2)
+		g_ptr_array_add (hints, (char *) hint2);
+
+	info->call = dbus_g_proxy_begin_call_with_timeout (info->proxy, "GetSecrets",
+	                                                   user_get_secrets_cb,
+	                                                   info,
+	                                                   NULL,
+	                                                   G_MAXINT32,
+	                                                   G_TYPE_STRING, setting_name,
+	                                                   DBUS_TYPE_G_ARRAY_OF_STRING, hints,
+	                                                   G_TYPE_BOOLEAN, request_new,
+	                                                   G_TYPE_INVALID);
+	g_ptr_array_free (hints, TRUE);
+	return info;
+}
+
+static void
+system_get_secrets_reply_cb (NMSettingsConnectionInterface *connection,
+                             GHashTable *secrets,
+                             GError *error,
+                             gpointer user_data)
+{
+	GetSecretsInfo *info = user_data;
+	GObject *provider;
+
+	provider = g_object_ref (info->provider);
+
+	nm_secrets_provider_interface_get_secrets_result (info->provider,
+	                                                  info->setting_name,
+	                                                  info->caller,
+	                                                  error ? NULL : secrets,
+	                                                  error);
+	free_get_secrets_info (info);
+	g_object_unref (provider);
+}
+
+static gboolean
+system_get_secrets_idle_cb (gpointer user_data)
+{
+	GetSecretsInfo *info = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (info->manager);
+	NMSettingsConnectionInterface *connection;
+	GError *error = NULL;
+	const char *hints[3] = { NULL, NULL, NULL };
+
+	info->idle_id = 0;
+
+	connection = nm_settings_interface_get_connection_by_path (NM_SETTINGS_INTERFACE (priv->sys_settings), 
+	                                                           info->connection_path);
+	if (!connection) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+		                             "unknown connection (not exported by system settings)");
+		nm_secrets_provider_interface_get_secrets_result (info->provider,
+		                                                  info->setting_name,
+		                                                  info->caller,
+		                                                  NULL,
+		                                                  error);
+		g_error_free (error);
+		free_get_secrets_info (info);
+		return FALSE;
+	}
+
+	hints[0] = info->hint1;
+	hints[1] = info->hint2;
+	nm_settings_connection_interface_get_secrets (connection,
+	                                              info->setting_name,
+	                                              hints,
+	                                              info->request_new,
+	                                              system_get_secrets_reply_cb,
+	                                              info);
+	return FALSE;
+}
+
+static GetSecretsInfo *
+system_get_secrets (NMManager *self,
+                    NMSecretsProviderInterface *provider,
+                    NMConnection *connection,
+                    const char *setting_name,
+                    gboolean request_new,
+                    RequestSecretsCaller caller_id,
+                    const char *hint1,
+                    const char *hint2)
+{
+	GetSecretsInfo *info;
+
+	info = g_malloc0 (sizeof (GetSecretsInfo));
+	info->manager = self;
+	info->provider = provider;
+	info->caller = caller_id;
+	info->setting_name = g_strdup (setting_name);
+	info->hint1 = hint1 ? g_strdup (hint1) : NULL;
+	info->hint2 = hint2 ? g_strdup (hint2) : NULL;
+	info->connection_path = g_strdup (nm_connection_get_path (connection));
+	info->request_new = request_new;
+
+	g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) free_get_secrets_info, info);
+
+	info->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+	                                 system_get_secrets_idle_cb,
+	                                 info,
+	                                 NULL);
+	return info;
+}
+
+static gboolean
+provider_get_secrets (NMSecretsProviderInterface *provider,
+                      NMConnection *connection,
+                      const char *setting_name,
+                      gboolean request_new,
+                      RequestSecretsCaller caller_id,
+                      const char *hint1,
+                      const char *hint2,
+                      gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GetSecretsInfo *info = NULL;
+	NMConnectionScope scope;
+	GSList *iter;
+
+	g_return_val_if_fail (connection != NULL, FALSE);
+	g_return_val_if_fail (setting_name != NULL, FALSE);
+
+	/* Tear down any pending secrets requests for this secrets provider */
+	for (iter = priv->secrets_calls; iter; iter = g_slist_next (iter)) {
+		GetSecretsInfo *candidate = iter->data;
+
+		if (provider == candidate->provider) {
+			free_get_secrets_info (candidate);
+			break;
+		}
+	}
+
+	/* Build up the new secrets request */
+	scope = nm_connection_get_scope (connection);
+	if (scope == NM_CONNECTION_SCOPE_SYSTEM) {
+		info = system_get_secrets (self, provider, connection, setting_name,
+		                           request_new, caller_id, hint1, hint2);
+	} else if (scope == NM_CONNECTION_SCOPE_USER) {
+		info = user_get_secrets (self, provider, connection, setting_name,
+		                         request_new, caller_id, hint1, hint2);
+	}
+
+	if (info)
+		priv->secrets_calls = g_slist_append (priv->secrets_calls, info);
+
+	return !!info;
+}
+
+static const char *
+internal_activate_device (NMManager *manager,
+                          NMDevice *device,
+                          NMConnection *connection,
+                          const char *specific_object,
+                          gboolean user_requested,
+                          gboolean assumed,
+                          GError **error)
+{
+	NMActRequest *req;
+	NMDeviceInterface *dev_iface;
+	gboolean success;
+
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+	dev_iface = NM_DEVICE_INTERFACE (device);
+
+	/* Ensure the requested connection is compatible with the device */
+	if (!nm_device_interface_check_connection_compatible (dev_iface, connection, error))
+		return NULL;
+
+	/* Tear down any existing connection */
+	if (nm_device_get_act_request (device)) {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_REASON_NONE);
+	}
+
+	req = nm_act_request_new (connection, specific_object, user_requested, assumed, (gpointer) device);
+	g_signal_connect (req, "manager-get-secrets", G_CALLBACK (provider_get_secrets), manager);
+	g_signal_connect (req, "manager-cancel-secrets", G_CALLBACK (provider_cancel_secrets), manager);
+	success = nm_device_interface_activate (dev_iface, req, error);
+	g_object_unref (req);
+
+	return success ? nm_act_request_get_active_connection_path (req) : NULL;
+}
+
+static gboolean
+wait_for_connection_expired (gpointer data)
+{
+	NMManager *manager = NM_MANAGER (data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	PendingConnectionInfo *info = priv->pending_connection_info;
+	GError *error = NULL;
+
+	g_return_val_if_fail (info != NULL, FALSE);
+
+	g_set_error (&error,
+	             NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+	             "%s", "Connection was not provided by any settings service");
+	nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate (timeout): (%d) %s",
+	             info->scope, info->connection_path, error->code, error->message);
+	dbus_g_method_return_error (info->context, error);
+	g_error_free (error);
+
+	info->timeout_id = 0;
+	pending_connection_info_destroy (priv->pending_connection_info);
+	priv->pending_connection_info = NULL;
+
+	return FALSE;
+}
+
+const char *
+nm_manager_activate_connection (NMManager *manager,
+                                NMConnection *connection,
+                                const char *specific_object,
+                                const char *device_path,
+                                gboolean user_requested,
+                                GError **error)
+{
+	NMManagerPrivate *priv;
+	NMDevice *device = NULL;
+	NMSettingConnection *s_con;
+	NMVPNConnection *vpn_connection;
+	const char *path;
+
+	g_return_val_if_fail (manager != NULL, NULL);
+	g_return_val_if_fail (connection != NULL, NULL);
+	g_return_val_if_fail (error != NULL, NULL);
+	g_return_val_if_fail (*error == NULL, NULL);
+
+	priv = NM_MANAGER_GET_PRIVATE (manager);
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	if (!strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_VPN_SETTING_NAME)) {
+		NMActRequest *req = NULL;
+		NMVPNManager *vpn_manager;
+
+		/* VPN connection */
+
+		if (specific_object) {
+			/* Find the specifc connection the client requested we use */
+			req = nm_manager_get_act_request_by_path (manager, specific_object, &device);
+			if (!req) {
+				g_set_error (error,
+				             NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
+				             "%s", "Base connection for VPN connection not active.");
+				return NULL;
+			}
+		} else {
+			GSList *iter;
+
+			/* Just find the current default connection */
+			for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+				NMDevice *candidate = NM_DEVICE (iter->data);
+				NMActRequest *candidate_req;
+
+				candidate_req = nm_device_get_act_request (candidate);
+				if (candidate_req && nm_act_request_get_default (candidate_req)) {
+					device = candidate;
+					req = candidate_req;
+					break;
+				}
+			}
+		}
+
+		if (!device || !req) {
+			g_set_error (error,
+			             NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			             "%s", "Could not find source connection, or the source connection had no active device.");
+			return NULL;
+		}
+
+		vpn_manager = nm_vpn_manager_get ();
+		vpn_connection = nm_vpn_manager_activate_connection (vpn_manager,
+		                                                     connection,
+		                                                     req,
+		                                                     device,
+		                                                     error);
+		g_signal_connect (vpn_connection, "manager-get-secrets",
+		                  G_CALLBACK (provider_get_secrets), manager);
+		g_signal_connect (vpn_connection, "manager-cancel-secrets",
+		                  G_CALLBACK (provider_cancel_secrets), manager);
+		path = nm_vpn_connection_get_active_connection_path (vpn_connection);
+		g_object_unref (vpn_manager);
+	} else {
+		NMDeviceState state;
+
+		/* Device-based connection */
+		device = nm_manager_get_device_by_path (manager, device_path);
+		if (!device) {
+			g_set_error (error,
+			             NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			             "%s", "Device not found");
+			return NULL;
+		}
+
+		state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device));
+		if (state < NM_DEVICE_STATE_DISCONNECTED) {
+			g_set_error (error,
+			             NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE,
+			             "%s", "Device not managed by NetworkManager");
+			return NULL;
+		}
+
+		path = internal_activate_device (manager,
+		                                 device,
+		                                 connection,
+		                                 specific_object,
+		                                 user_requested,
+		                                 FALSE,
+		                                 error);
+	}
+
+	return path;
+}
+
+static void
+connection_added_default_handler (NMManager *manager,
+						    NMConnection *connection,
+						    NMConnectionScope scope)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	PendingConnectionInfo *info = priv->pending_connection_info;
+	const char *path;
+	GError *error = NULL;
+
+	if (!info)
+		return;
+
+	if (scope != info->scope)
+		return;
+
+	if (strcmp (info->connection_path, nm_connection_get_path (connection)))
+		return;
+
+	/* Will destroy below; can't be valid during the initial activation start */
+	priv->pending_connection_info = NULL;
+
+	path = nm_manager_activate_connection (manager,
+	                                       connection,
+	                                       info->specific_object_path,
+	                                       info->device_path,
+	                                       TRUE,
+	                                       &error);
+	if (path) {
+		dbus_g_method_return (info->context, path);
+		g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS);
+	} else {
+		dbus_g_method_return_error (info->context, error);
+		nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s",
+		             scope, info->connection_path, error->code, error->message);
+		g_error_free (error);
+	}
+
+	pending_connection_info_destroy (info);
+}
+
+static gboolean
+is_user_request_authorized (NMManager *manager,
+                            DBusGMethodInvocation *context,
+                            GError **error)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	DBusConnection *connection;
+	char *sender = NULL;
+	gulong sender_uid = G_MAXULONG;
+	DBusError dbus_error;
+	char *service_owner = NULL;
+	const char *service_name;
+	gulong service_uid = G_MAXULONG;
+	gboolean success = FALSE;
+
+	/* Ensure the request to activate the user connection came from the
+	 * same session as the user settings service.  FIXME: use ConsoleKit
+	 * too.
+	 */
+	if (!priv->user_proxy) {
+		g_set_error (error, NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_INVALID_SERVICE,
+		             "%s", "No user settings service available");
+		goto out;
+	}
+
+	sender = dbus_g_method_get_sender (context);
+	if (!sender) {
+		g_set_error (error, NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		             "%s", "Could not determine D-Bus requestor");
+		goto out;
+	}
+
+	connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr);
+	if (!connection) {
+		g_set_error (error, NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		             "%s", "Could not get the D-Bus system bus");
+		goto out;
+	}
+
+	dbus_error_init (&dbus_error);
+	/* FIXME: do this async */
+	sender_uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
+	if (dbus_error_is_set (&dbus_error)) {
+		dbus_error_free (&dbus_error);
+		g_set_error (error, NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		             "%s", "Could not determine the Unix user ID of the requestor");
+		goto out;
+	}
+
+	/* Let root activate anything.
+	 * FIXME: use a PolicyKit permission instead
+	 */
+	if (0 == sender_uid) {
+		success = TRUE;
+		goto out;
+	}
+
+	service_name = dbus_g_proxy_get_bus_name (priv->user_proxy);
+	if (!service_name) {
+		g_set_error (error, NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		             "%s", "Could not determine user settings service name");
+		goto out;
+	}
+
+	service_owner = nm_dbus_manager_get_name_owner (priv->dbus_mgr, service_name, NULL);
+	if (!service_owner) {
+		g_set_error (error, NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		             "%s", "Could not determine D-Bus owner of the user settings service");
+		goto out;
+	}
+
+	dbus_error_init (&dbus_error);
+	/* FIXME: do this async */
+	service_uid = dbus_bus_get_unix_user (connection, service_owner, &dbus_error);
+	if (dbus_error_is_set (&dbus_error)) {
+		dbus_error_free (&dbus_error);
+		g_set_error (error, NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		             "%s", "Could not determine the Unix UID of the sender of the request");
+		goto out;
+	}
+
+	/* And finally, the actual UID check */
+	if (sender_uid != service_uid) {
+		g_set_error (error, NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		             "%s", "Requestor UID does not match the UID of the user settings service");
+		goto out;
+	}
+
+	success = TRUE;
+
+out:
+	g_free (sender);
+	g_free (service_owner);
+	return success;
+}
+
+static void
+impl_manager_activate_connection (NMManager *manager,
+                                  const char *service_name,
+                                  const char *connection_path,
+                                  const char *device_path,
+                                  const char *specific_object_path,
+                                  DBusGMethodInvocation *context)
+{
+	NMConnectionScope scope = NM_CONNECTION_SCOPE_UNKNOWN;
+	NMConnection *connection;
+	GError *error = NULL;
+	char *real_sop = NULL;
+	char *path = NULL;
+
+	if (!strcmp (service_name, NM_DBUS_SERVICE_USER_SETTINGS)) {
+		if (!is_user_request_authorized (manager, context, &error))
+			goto err;
+
+		scope = NM_CONNECTION_SCOPE_USER;
+	} else if (!strcmp (service_name, NM_DBUS_SERVICE_SYSTEM_SETTINGS))
+		scope = NM_CONNECTION_SCOPE_SYSTEM;
+	else {
+		g_set_error (&error,
+		             NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_SERVICE,
+		             "%s", "Invalid settings service name");
+		goto err;
+	}
+
+	/* "/" is special-cased to NULL to get through D-Bus */
+	if (specific_object_path && strcmp (specific_object_path, "/"))
+		real_sop = g_strdup (specific_object_path);
+
+	connection = nm_manager_get_connection_by_object_path (manager, scope, connection_path);
+	if (connection) {
+		path = (char *) nm_manager_activate_connection (manager,
+		                                                connection,
+		                                                real_sop,
+		                                                device_path,
+		                                                TRUE,
+		                                                &error);
+		if (path) {
+			dbus_g_method_return (context, path);
+			g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS);
+		}
+	} else {
+		PendingConnectionInfo *info;
+		NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+
+		if (priv->pending_connection_info) {
+			pending_connection_info_destroy (priv->pending_connection_info);
+			priv->pending_connection_info = NULL;
+		}
+
+		/* Don't have the connection quite yet, probably created by
+		 * the client on-the-fly.  Defer the activation until we have it
+		 */
+
+		info = g_slice_new0 (PendingConnectionInfo);
+		info->context = context;
+		info->device_path = g_strdup (device_path);
+		info->scope = scope;
+		info->connection_path = g_strdup (connection_path);
+		info->specific_object_path = g_strdup (real_sop);
+		info->timeout_id = g_timeout_add_seconds (5, wait_for_connection_expired, manager);
+
+		// FIXME: should probably be per-device, not global to the manager
+		NM_MANAGER_GET_PRIVATE (manager)->pending_connection_info = info;
+	}
+
+ err:
+	if (error) {
+		dbus_g_method_return_error (context, error);
+		nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s",
+		             scope, connection_path, error->code, error->message);
+		g_error_free (error);
+	}
+
+	g_free (real_sop);
+}
+
+gboolean
+nm_manager_deactivate_connection (NMManager *manager,
+                                  const char *connection_path,
+                                  NMDeviceStateReason reason,
+                                  GError **error)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMVPNManager *vpn_manager;
+	GSList *iter;
+	gboolean success = FALSE;
+	NMVPNConnectionStateReason vpn_reason = NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED;
+
+	/* Check for device connections first */
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *device = NM_DEVICE (iter->data);
+		NMActRequest *req;
+
+		req = nm_device_get_act_request (device);
+		if (!req)
+			continue;
+
+		if (!strcmp (connection_path, nm_act_request_get_active_connection_path (req))) {
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_DISCONNECTED,
+			                         reason);
+			success = TRUE;
+			goto done;
+		}
+	}
+
+	/* Check for VPN connections next */
+	vpn_manager = nm_vpn_manager_get ();
+	if (reason == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED)
+		vpn_reason = NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED;
+	if (nm_vpn_manager_deactivate_connection (vpn_manager, connection_path, vpn_reason)) {
+		success = TRUE;
+	} else {
+		g_set_error (error,
+		             NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
+		             "%s", "The connection was not active.");
+	}
+	g_object_unref (vpn_manager);
+
+done:
+	g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS);
+	return success;
+}
+
+static gboolean
+impl_manager_deactivate_connection (NMManager *manager,
+                                    const char *connection_path,
+                                    GError **error)
+{
+	return nm_manager_deactivate_connection (manager,
+	                                         connection_path,
+	                                         NM_DEVICE_STATE_REASON_USER_REQUESTED,
+	                                         error);
+}
+
+static void
+do_sleep_wake (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	const GSList *unmanaged_specs;
+	GSList *iter;
+
+	if (manager_sleeping (self)) {
+		nm_log_info (LOGD_SUSPEND, "sleeping or disabling...");
+
+		/* Just deactivate and down all devices from the device list,
+		 * to keep things fast the device list will get resynced when
+		 * the manager wakes up.
+		 */
+		for (iter = priv->devices; iter; iter = iter->next)
+			nm_device_set_managed (NM_DEVICE (iter->data), FALSE, NM_DEVICE_STATE_REASON_SLEEPING);
+
+	} else {
+		nm_log_info (LOGD_SUSPEND, "waking up and re-enabling...");
+
+		unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings);
+
+		/* Ensure rfkill state is up-to-date since we don't respond to state
+		 * changes during sleep.
+		 */
+		nm_manager_rfkill_update (self, RFKILL_TYPE_UNKNOWN);
+
+		/* Re-manage managed devices */
+		for (iter = priv->devices; iter; iter = iter->next) {
+			NMDevice *device = NM_DEVICE (iter->data);
+			guint i;
+
+			/* enable/disable wireless devices since that we don't respond
+			 * to killswitch changes during sleep.
+			 */
+			for (i = 0; i < RFKILL_TYPE_MAX; i++) {
+				RadioState *rstate = &priv->radio_states[i];
+				gboolean enabled = (rstate->hw_enabled && rstate->enabled);
+				RfKillType devtype = RFKILL_TYPE_UNKNOWN;
+
+				if (rstate->desc) {
+					nm_log_dbg (LOGD_RFKILL, "%s %s devices (hw_enabled %d, enabled %d)",
+					            enabled ? "enabling" : "disabling",
+					            rstate->desc, rstate->hw_enabled, rstate->enabled);
+				}
+
+				g_object_get (G_OBJECT (device), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL);
+				if (devtype == rstate->rtype)
+					nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), enabled);
+			}
+
+			nm_device_clear_autoconnect_inhibit (device);
+			if (nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs))
+				nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
+			else
+				nm_device_set_managed (device, TRUE, NM_DEVICE_STATE_REASON_NOW_MANAGED);
+		}
+
+		/* Ask for new bluetooth devices */
+		bluez_manager_resync_devices (self);
+	}
+
+	nm_manager_update_state (self);
+}
+
+static gboolean
+impl_manager_sleep (NMManager *self, gboolean do_sleep, GError **error)
+{
+	NMManagerPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
+
+	priv = NM_MANAGER_GET_PRIVATE (self);
+
+	if (priv->sleeping == do_sleep) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE,
+		             "Already %s", do_sleep ? "asleep" : "awake");
+		return FALSE;
+	}
+
+	nm_log_info (LOGD_SUSPEND, "%s requested (sleeping: %s  enabled: %s)",
+	             do_sleep ? "sleep" : "wake",
+	             priv->sleeping ? "yes" : "no",
+	             priv->net_enabled ? "yes" : "no");
+
+	priv->sleeping = do_sleep;
+
+	do_sleep_wake (self);
+
+	g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING);
+	return TRUE;
+}
+
+static gboolean
+impl_manager_enable (NMManager *self, gboolean enable, GError **error)
+{
+	NMManagerPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
+
+	priv = NM_MANAGER_GET_PRIVATE (self);
+
+	if (priv->net_enabled == enable) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED,
+		             "Already %s", enable ? "enabled" : "disabled");
+		return FALSE;
+	}
+
+	/* Update "NetworkingEnabled" key in state file */
+	if (priv->state_file) {
+		GError *err = NULL;
+
+		if (!write_value_to_state_file (priv->state_file,
+		                                "main", "NetworkingEnabled",
+		                                G_TYPE_BOOLEAN, (gpointer) &enable,
+		                                &err)) {
+			/* Not a hard error */
+			nm_log_warn (LOGD_SUSPEND, "writing to state file %s failed: (%d) %s.",
+			             priv->state_file,
+			             err ? err->code : -1,
+			             (err && err->message) ? err->message : "unknown");
+		}
+	}
+
+	nm_log_info (LOGD_SUSPEND, "%s requested (sleeping: %s  enabled: %s)",
+	             enable ? "enable" : "disable",
+	             priv->sleeping ? "yes" : "no",
+	             priv->net_enabled ? "yes" : "no");
+
+	priv->net_enabled = enable;
+
+	do_sleep_wake (self);
+
+	g_object_notify (G_OBJECT (self), NM_MANAGER_NETWORKING_ENABLED);
+	return TRUE;
+}
+
+/* Legacy 0.6 compatibility interface */
+
+static gboolean
+impl_manager_legacy_sleep (NMManager *manager, GError **error)
+{
+	return impl_manager_sleep (manager, TRUE, error);
+}
+
+static gboolean
+impl_manager_legacy_wake  (NMManager *manager, GError **error)
+{
+	return impl_manager_sleep (manager, FALSE, error);
+}
+
+static gboolean
+impl_manager_legacy_state (NMManager *manager, guint32 *state, GError **err)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+
+	nm_manager_update_state (manager);
+	*state = priv->state;
+	return TRUE;
+}
+
+static gboolean
+impl_manager_set_logging (NMManager *manager,
+                          const char *level,
+                          const char *domains,
+                          GError **error)
+{
+	if (nm_logging_setup (level, domains, error)) {
+		char *new_domains = nm_logging_domains_to_string ();
+
+		nm_log_info (LOGD_CORE, "logging: level '%s' domains '%s'",
+		             nm_logging_level_to_string (),
+		             new_domains);
+		g_free (new_domains);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/* Connections */
+
+static int
+connection_sort (gconstpointer pa, gconstpointer pb)
+{
+	NMConnection *a = NM_CONNECTION (pa);
+	NMSettingConnection *con_a;
+	NMConnection *b = NM_CONNECTION (pb);
+	NMSettingConnection *con_b;
+
+	con_a = (NMSettingConnection *) nm_connection_get_setting (a, NM_TYPE_SETTING_CONNECTION);
+	g_assert (con_a);
+	con_b = (NMSettingConnection *) nm_connection_get_setting (b, NM_TYPE_SETTING_CONNECTION);
+	g_assert (con_b);
+
+	if (nm_setting_connection_get_autoconnect (con_a) != nm_setting_connection_get_autoconnect (con_b)) {
+		if (nm_setting_connection_get_autoconnect (con_a))
+			return -1;
+		return 1;
+	}
+
+	if (nm_setting_connection_get_timestamp (con_a) > nm_setting_connection_get_timestamp (con_b))
+		return -1;
+	else if (nm_setting_connection_get_timestamp (con_a) == nm_setting_connection_get_timestamp (con_b))
+		return 0;
+	return 1;
+}
+
+static void
+connections_to_slist (gpointer key, gpointer value, gpointer user_data)
+{
+	GSList **list = (GSList **) user_data;
+
+	*list = g_slist_insert_sorted (*list, g_object_ref (value), connection_sort);
+}
+
+/* Returns a GSList of referenced NMConnection objects, caller must
+ * unref the connections in the list and destroy the list.
+ */
+GSList *
+nm_manager_get_connections (NMManager *manager,
+                            NMConnectionScope scope)
+{
+	NMManagerPrivate *priv;
+	GSList *list = NULL;
+
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+
+	priv = NM_MANAGER_GET_PRIVATE (manager);
+	if (scope == NM_CONNECTION_SCOPE_USER)
+		g_hash_table_foreach (priv->user_connections, connections_to_slist, &list);
+	else if (scope == NM_CONNECTION_SCOPE_SYSTEM)
+		g_hash_table_foreach (priv->system_connections, connections_to_slist, &list);
+	else
+		nm_log_err (LOGD_CORE, "unknown NMConnectionScope %d", scope);
+	return list;
+}
+
+NMConnection *
+nm_manager_get_connection_by_object_path (NMManager *manager,
+                                          NMConnectionScope scope,
+                                          const char *path)
+{
+	NMManagerPrivate *priv;
+	NMConnection *connection = NULL;
+
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+	g_return_val_if_fail (path != NULL, NULL);
+
+	priv = NM_MANAGER_GET_PRIVATE (manager);
+	if (scope == NM_CONNECTION_SCOPE_USER)
+		connection = (NMConnection *) g_hash_table_lookup (priv->user_connections, path);
+	else if (scope == NM_CONNECTION_SCOPE_SYSTEM)
+		connection = (NMConnection *) g_hash_table_lookup (priv->system_connections, path);
+	else
+		nm_log_err (LOGD_CORE, "unknown NMConnectionScope %d", scope);
+	return connection;
+}
+
+GPtrArray *
+nm_manager_get_active_connections_by_connection (NMManager *manager,
+                                                 NMConnection *connection)
+{
+	return get_active_connections (manager, connection);
+}
+
+void
+nm_manager_start (NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	guint i;
+
+	/* Set initial radio enabled/disabled state */
+	for (i = 0; i < RFKILL_TYPE_MAX; i++) {
+		RadioState *rstate = &priv->radio_states[i];
+		gboolean enabled = TRUE, hw_enabled = TRUE;
+
+		if (!rstate->desc)
+			continue;
+
+		switch (nm_udev_manager_get_rfkill_state (priv->udev_mgr, i)) {
+		case RFKILL_UNBLOCKED:
+			enabled = TRUE;
+			hw_enabled = TRUE;
+			break;
+		case RFKILL_SOFT_BLOCKED:
+			enabled = FALSE;
+			hw_enabled = TRUE;
+			break;
+		case RFKILL_HARD_BLOCKED:
+			enabled = FALSE;
+			hw_enabled = FALSE;
+			break;
+		default:
+			break;
+		}
+
+		rstate->hw_enabled = hw_enabled;
+		nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file",
+		             rstate->desc,
+		             (rstate->hw_enabled && enabled) ? "enabled" : "disabled",
+		             (rstate->enabled) ? "enabled" : "disabled");
+		manager_set_radio_enabled (self, rstate, rstate->enabled && enabled);
+	}
+
+	/* Log overall networking status - enabled/disabled */
+	nm_log_info (LOGD_CORE, "Networking is %s by state file",
+	             priv->net_enabled ? "enabled" : "disabled");
+
+	system_unmanaged_devices_changed_cb (priv->sys_settings, NULL, self);
+	system_hostname_changed_cb (priv->sys_settings, NULL, self);
+	system_query_connections (self);
+
+	/* Get user connections if the user settings service is around, otherwise
+	 * they will be queried when the user settings service shows up on the
+	 * bus in nm_manager_name_owner_changed().
+	 */
+	if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, NM_DBUS_SERVICE_USER_SETTINGS))
+		user_query_connections (self);
+
+	nm_udev_manager_query_devices (priv->udev_mgr);
+	bluez_manager_resync_devices (self);
+}
+
+NMManager *
+nm_manager_get (const char *config_file,
+                const char *plugins,
+                const char *state_file,
+                gboolean initial_net_enabled,
+                gboolean initial_wifi_enabled,
+                gboolean initial_wwan_enabled,
+                GError **error)
+{
+	static NMManager *singleton = NULL;
+	NMManagerPrivate *priv;
+	DBusGConnection *bus;
+
+	if (singleton)
+		return g_object_ref (singleton);
+
+	singleton = (NMManager *) g_object_new (NM_TYPE_MANAGER, NULL);
+	g_assert (singleton);
+
+	priv = NM_MANAGER_GET_PRIVATE (singleton);
+
+	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	g_assert (bus);
+
+	priv->sys_settings = nm_sysconfig_settings_new (config_file, plugins, bus, error);
+	if (!priv->sys_settings) {
+		g_object_unref (singleton);
+		return NULL;
+	}
+	nm_settings_service_export (NM_SETTINGS_SERVICE (priv->sys_settings));
+
+	priv->config_file = g_strdup (config_file);
+
+	priv->state_file = g_strdup (state_file);
+
+	priv->net_enabled = initial_net_enabled;
+
+	priv->radio_states[RFKILL_TYPE_WLAN].enabled = initial_wifi_enabled;
+	priv->radio_states[RFKILL_TYPE_WWAN].enabled = initial_wwan_enabled;
+
+	g_signal_connect (priv->sys_settings, "notify::" NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS,
+	                  G_CALLBACK (system_unmanaged_devices_changed_cb), singleton);
+	g_signal_connect (priv->sys_settings, "notify::" NM_SETTINGS_SYSTEM_INTERFACE_HOSTNAME,
+	                  G_CALLBACK (system_hostname_changed_cb), singleton);
+	g_signal_connect (priv->sys_settings, "new-connection",
+	                  G_CALLBACK (system_new_connection_cb), singleton);
+
+	dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (priv->dbus_mgr),
+	                                     NM_DBUS_PATH,
+	                                     G_OBJECT (singleton));
+
+	g_signal_connect (priv->dbus_mgr,
+	                  "name-owner-changed",
+	                  G_CALLBACK (nm_manager_name_owner_changed),
+	                  singleton);
+
+	priv->udev_mgr = nm_udev_manager_new ();
+	g_signal_connect (priv->udev_mgr,
+	                  "device-added",
+	                  G_CALLBACK (udev_device_added_cb),
+	                  singleton);
+	g_signal_connect (priv->udev_mgr,
+	                  "device-removed",
+	                  G_CALLBACK (udev_device_removed_cb),
+	                  singleton);
+	g_signal_connect (priv->udev_mgr,
+	                  "rfkill-changed",
+	                  G_CALLBACK (udev_manager_rfkill_changed_cb),
+	                  singleton);
+
+	priv->bluez_mgr = nm_bluez_manager_get ();
+
+	g_signal_connect (priv->bluez_mgr,
+			  "bdaddr-added",
+			  G_CALLBACK (bluez_manager_bdaddr_added_cb),
+			  singleton);
+
+	g_signal_connect (priv->bluez_mgr,
+			  "bdaddr-removed",
+			  G_CALLBACK (bluez_manager_bdaddr_removed_cb),
+			  singleton);
+
+	return singleton;
+}
+
+static void
+dispose (GObject *object)
+{
+	NMManager *manager = NM_MANAGER (object);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+
+	if (priv->disposed) {
+		G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
+		return;
+	}
+	priv->disposed = TRUE;
+
+	pending_connection_info_destroy (priv->pending_connection_info);
+	priv->pending_connection_info = NULL;
+
+	while (g_slist_length (priv->secrets_calls))
+		free_get_secrets_info ((GetSecretsInfo *) priv->secrets_calls->data);
+
+	while (g_slist_length (priv->devices)) {
+		priv->devices = remove_one_device (manager,
+		                                   priv->devices,
+		                                   NM_DEVICE (priv->devices->data),
+		                                   TRUE,
+		                                   FALSE);
+	}
+
+	user_destroy_connections (manager);
+	g_hash_table_destroy (priv->user_connections);
+	priv->user_connections = NULL;
+
+	g_hash_table_foreach (priv->system_connections, emit_removed, manager);
+	g_hash_table_remove_all (priv->system_connections);
+	g_hash_table_destroy (priv->system_connections);
+	priv->system_connections = NULL;
+
+	g_free (priv->hostname);
+	g_free (priv->config_file);
+
+	if (priv->sys_settings) {
+		g_object_unref (priv->sys_settings);
+		priv->sys_settings = NULL;
+	}
+
+	if (priv->vpn_manager_id) {
+		g_source_remove (priv->vpn_manager_id);
+		priv->vpn_manager_id = 0;
+	}
+	g_object_unref (priv->vpn_manager);
+
+	if (priv->modem_added_id) {
+		g_source_remove (priv->modem_added_id);
+		priv->modem_added_id = 0;
+	}
+	if (priv->modem_removed_id) {
+		g_source_remove (priv->modem_removed_id);
+		priv->modem_removed_id = 0;
+	}
+	g_object_unref (priv->modem_manager);
+
+	g_object_unref (priv->dbus_mgr);
+	if (priv->bluez_mgr)
+		g_object_unref (priv->bluez_mgr);
+
+	G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	NMManager *self = NM_MANAGER (object);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_NETWORKING_ENABLED:
+		/* Construct only for now */
+		priv->net_enabled = g_value_get_boolean (value);
+		break;
+	case PROP_WIRELESS_ENABLED:
+		manager_set_radio_enabled (NM_MANAGER (object),
+		                           &priv->radio_states[RFKILL_TYPE_WLAN],
+		                           g_value_get_boolean (value));
+		break;
+	case PROP_WWAN_ENABLED:
+		manager_set_radio_enabled (NM_MANAGER (object),
+		                           &priv->radio_states[RFKILL_TYPE_WWAN],
+		                           g_value_get_boolean (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMManager *self = NM_MANAGER (object);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_STATE:
+		nm_manager_update_state (self);
+		g_value_set_uint (value, priv->state);
+		break;
+	case PROP_NETWORKING_ENABLED:
+		g_value_set_boolean (value, priv->net_enabled);
+		break;
+	case PROP_WIRELESS_ENABLED:
+		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].enabled);
+		break;
+	case PROP_WIRELESS_HARDWARE_ENABLED:
+		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled);
+		break;
+	case PROP_WWAN_ENABLED:
+		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].enabled);
+		break;
+	case PROP_WWAN_HARDWARE_ENABLED:
+		g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled);
+		break;
+	case PROP_ACTIVE_CONNECTIONS:
+		g_value_take_boxed (value, get_active_connections (self, NULL));
+		break;
+	case PROP_HOSTNAME:
+		g_value_set_string (value, priv->hostname);
+		break;
+	case PROP_SLEEPING:
+		g_value_set_boolean (value, priv->sleeping);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_manager_init (NMManager *manager)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	DBusGConnection *g_connection;
+	guint id, i;
+
+	/* Initialize rfkill structures and states */
+	memset (priv->radio_states, 0, sizeof (priv->radio_states));
+
+	priv->radio_states[RFKILL_TYPE_WLAN].enabled = TRUE;
+	priv->radio_states[RFKILL_TYPE_WLAN].key = "WirelessEnabled";
+	priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED;
+	priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED;
+	priv->radio_states[RFKILL_TYPE_WLAN].desc = "WiFi";
+	priv->radio_states[RFKILL_TYPE_WLAN].other_enabled_func = nm_manager_get_ipw_rfkill_state;
+	priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN;
+
+	priv->radio_states[RFKILL_TYPE_WWAN].enabled = TRUE;
+	priv->radio_states[RFKILL_TYPE_WWAN].key = "WWANEnabled";
+	priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED;
+	priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED;
+	priv->radio_states[RFKILL_TYPE_WWAN].desc = "WWAN";
+	priv->radio_states[RFKILL_TYPE_WWAN].other_enabled_func = nm_manager_get_modem_enabled_state;
+	priv->radio_states[RFKILL_TYPE_WWAN].rtype = RFKILL_TYPE_WWAN;
+
+	priv->radio_states[RFKILL_TYPE_WIMAX].enabled = TRUE;
+	priv->radio_states[RFKILL_TYPE_WIMAX].key = "WiMAXEnabled";
+	priv->radio_states[RFKILL_TYPE_WIMAX].prop = NULL;
+	priv->radio_states[RFKILL_TYPE_WIMAX].hw_prop = NULL;
+	priv->radio_states[RFKILL_TYPE_WIMAX].desc = "WiMAX";
+	priv->radio_states[RFKILL_TYPE_WIMAX].other_enabled_func = NULL;
+	priv->radio_states[RFKILL_TYPE_WIMAX].rtype = RFKILL_TYPE_WIMAX;
+
+	for (i = 0; i < RFKILL_TYPE_MAX; i++)
+		priv->radio_states[i].hw_enabled = TRUE;
+
+	priv->sleeping = FALSE;
+	priv->state = NM_STATE_DISCONNECTED;
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+
+	priv->user_connections = g_hash_table_new_full (g_str_hash,
+	                                                g_str_equal,
+	                                                g_free,
+	                                                g_object_unref);
+
+	priv->system_connections = g_hash_table_new_full (g_str_hash,
+	                                                  g_str_equal,
+	                                                  g_free,
+	                                                  g_object_unref);
+
+	priv->modem_manager = nm_modem_manager_get ();
+	priv->modem_added_id = g_signal_connect (priv->modem_manager, "modem-added",
+	                                         G_CALLBACK (modem_added), manager);
+	priv->modem_removed_id = g_signal_connect (priv->modem_manager, "modem-removed",
+	                                           G_CALLBACK (modem_removed), manager);
+
+	priv->vpn_manager = nm_vpn_manager_get ();
+	id = g_signal_connect (G_OBJECT (priv->vpn_manager), "connection-deactivated",
+	                       G_CALLBACK (vpn_manager_connection_deactivated_cb), manager);
+	priv->vpn_manager_id = id;
+
+	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+
+	/* avahi-autoipd stuff */
+	priv->aipd_proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                              NM_AUTOIP_DBUS_SERVICE,
+	                                              "/",
+	                                              NM_AUTOIP_DBUS_IFACE);
+	if (priv->aipd_proxy) {
+		dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING_STRING,
+		                                   G_TYPE_NONE,
+		                                   G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+		                                   G_TYPE_INVALID);
+
+		dbus_g_proxy_add_signal (priv->aipd_proxy,
+		                         "Event",
+		                         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+		                         G_TYPE_INVALID);
+
+		dbus_g_proxy_connect_signal (priv->aipd_proxy, "Event",
+		                             G_CALLBACK (aipd_handle_event),
+		                             manager,
+		                             NULL);
+	} else
+		nm_log_warn (LOGD_AUTOIP4, "could not initialize avahi-autoipd D-Bus proxy");
+}
+
+static void
+nm_manager_class_init (NMManagerClass *manager_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+	g_type_class_add_private (manager_class, sizeof (NMManagerPrivate));
+
+	/* virtual methods */
+	manager_class->connection_added = connection_added_default_handler;
+
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->dispose = dispose;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_STATE,
+		 g_param_spec_uint (NM_MANAGER_STATE,
+		                    "State",
+		                    "Current state",
+		                    0, NM_STATE_DISCONNECTED, 0,
+		                    G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_NETWORKING_ENABLED,
+		 g_param_spec_boolean (NM_MANAGER_NETWORKING_ENABLED,
+		                       "NetworkingEnabled",
+		                       "Is networking enabled",
+		                       TRUE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_WIRELESS_ENABLED,
+		 g_param_spec_boolean (NM_MANAGER_WIRELESS_ENABLED,
+		                       "WirelessEnabled",
+		                       "Is wireless enabled",
+		                       TRUE,
+		                       G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_WIRELESS_HARDWARE_ENABLED,
+		 g_param_spec_boolean (NM_MANAGER_WIRELESS_HARDWARE_ENABLED,
+		                       "WirelessHardwareEnabled",
+		                       "RF kill state",
+		                       TRUE,
+		                       G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_WWAN_ENABLED,
+		 g_param_spec_boolean (NM_MANAGER_WWAN_ENABLED,
+		                       "WwanEnabled",
+		                       "Is mobile broadband enabled",
+		                       TRUE,
+		                       G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_WWAN_HARDWARE_ENABLED,
+		 g_param_spec_boolean (NM_MANAGER_WWAN_HARDWARE_ENABLED,
+		                       "WwanHardwareEnabled",
+		                       "Whether WWAN is disabled by a hardware switch or not",
+		                       TRUE,
+		                       G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE_CONNECTIONS,
+		 g_param_spec_boxed (NM_MANAGER_ACTIVE_CONNECTIONS,
+		                     "Active connections",
+		                     "Active connections",
+		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+		                     G_PARAM_READABLE));
+
+	/* Hostname is not exported over D-Bus */
+	g_object_class_install_property
+		(object_class, PROP_HOSTNAME,
+		 g_param_spec_string (NM_MANAGER_HOSTNAME,
+		                      "Hostname",
+		                      "Hostname",
+		                      NULL,
+		                      G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	/* Sleeping is not exported over D-Bus */
+	g_object_class_install_property
+		(object_class, PROP_SLEEPING,
+		 g_param_spec_boolean (NM_MANAGER_SLEEPING,
+		                       "Sleeping",
+		                       "Sleeping",
+		                       FALSE,
+		                       G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	/* signals */
+	signals[DEVICE_ADDED] =
+		g_signal_new ("device-added",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMManagerClass, device_added),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__OBJECT,
+		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+	signals[DEVICE_REMOVED] =
+		g_signal_new ("device-removed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMManagerClass, device_removed),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__OBJECT,
+		              G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+	signals[STATE_CHANGED] =
+		g_signal_new ("state-changed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMManagerClass, state_changed),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__UINT,
+		              G_TYPE_NONE, 1, G_TYPE_UINT);
+
+	signals[PROPERTIES_CHANGED] =
+		nm_properties_changed_signal_new (object_class,
+		                                  G_STRUCT_OFFSET (NMManagerClass, properties_changed));
+
+	signals[CONNECTIONS_ADDED] =
+		g_signal_new ("connections-added",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMManagerClass, connections_added),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__UINT,
+		              G_TYPE_NONE, 1, G_TYPE_UINT);
+
+	signals[CONNECTION_ADDED] =
+		g_signal_new ("connection-added",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMManagerClass, connection_added),
+		              NULL, NULL,
+		              _nm_marshal_VOID__OBJECT_UINT,
+		              G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT);
+
+	signals[CONNECTION_UPDATED] =
+		g_signal_new ("connection-updated",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMManagerClass, connection_updated),
+		              NULL, NULL,
+		              _nm_marshal_VOID__OBJECT_UINT,
+		              G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT);
+
+	signals[CONNECTION_REMOVED] =
+		g_signal_new ("connection-removed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMManagerClass, connection_removed),
+		              NULL, NULL,
+		              _nm_marshal_VOID__OBJECT_UINT,
+		              G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT);
+
+	/* StateChange is DEPRECATED */
+	signals[STATE_CHANGE] =
+		g_signal_new ("state-change",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              0, NULL, NULL,
+		              g_cclosure_marshal_VOID__UINT,
+		              G_TYPE_NONE, 1, G_TYPE_UINT);
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class),
+	                                 &dbus_glib_nm_manager_object_info);
+
+	dbus_g_error_domain_register (NM_MANAGER_ERROR, NULL, NM_TYPE_MANAGER_ERROR);
+	dbus_g_error_domain_register (NM_LOGGING_ERROR, "org.freedesktop.NetworkManager.Logging", NM_TYPE_LOGGING_ERROR);
+}
+
diff --git a/src/nm-manager.h b/src/nm-manager.h
new file mode 100644
index 00000000..0f4d72f2
--- /dev/null
+++ b/src/nm-manager.h
@@ -0,0 +1,121 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_MANAGER_H
+#define NM_MANAGER_H 1
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include "nm-device.h"
+#include "nm-device-interface.h"
+
+#define NM_TYPE_MANAGER            (nm_manager_get_type ())
+#define NM_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MANAGER, NMManager))
+#define NM_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MANAGER, NMManagerClass))
+#define NM_IS_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MANAGER))
+#define NM_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_MANAGER))
+#define NM_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MANAGER, NMManagerClass))
+
+#define NM_MANAGER_STATE "state"
+#define NM_MANAGER_NETWORKING_ENABLED "networking-enabled"
+#define NM_MANAGER_WIRELESS_ENABLED "wireless-enabled"
+#define NM_MANAGER_WIRELESS_HARDWARE_ENABLED "wireless-hardware-enabled"
+#define NM_MANAGER_WWAN_ENABLED "wwan-enabled"
+#define NM_MANAGER_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled"
+#define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections"
+
+/* Not exported */
+#define NM_MANAGER_HOSTNAME "hostname"
+#define NM_MANAGER_SLEEPING "sleeping"
+
+typedef struct {
+	GObject parent;
+} NMManager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*device_added) (NMManager *manager, NMDevice *device);
+	void (*device_removed) (NMManager *manager, NMDevice *device);
+	void (*state_changed) (NMManager *manager, guint state);
+	void (*properties_changed) (NMManager *manager, GHashTable *properties);
+
+	void (*connections_added) (NMManager *manager, NMConnectionScope scope);
+
+	void (*connection_added) (NMManager *manager,
+				  NMConnection *connection,
+				  NMConnectionScope scope);
+
+	void (*connection_updated) (NMManager *manager,
+				  NMConnection *connection,
+				  NMConnectionScope scope);
+
+	void (*connection_removed) (NMManager *manager,
+				    NMConnection *connection,
+				    NMConnectionScope scope);
+} NMManagerClass;
+
+GType nm_manager_get_type (void);
+
+NMManager *nm_manager_get (const char *config_file,
+                           const char *plugins,
+                           const char *state_file,
+                           gboolean initial_net_enabled,
+                           gboolean initial_wifi_enabled,
+                           gboolean initial_wwan_enabled,
+                           GError **error);
+
+void nm_manager_start (NMManager *manager);
+
+/* Device handling */
+
+GSList *nm_manager_get_devices (NMManager *manager);
+
+const char * nm_manager_activate_connection (NMManager *manager,
+                                             NMConnection *connection,
+                                             const char *specific_object,
+                                             const char *device_path,
+                                             gboolean user_requested,
+                                             GError **error);
+
+gboolean nm_manager_deactivate_connection (NMManager *manager,
+                                           const char *connection_path,
+                                           NMDeviceStateReason reason,
+                                           GError **error);
+
+/* State handling */
+
+NMState nm_manager_get_state (NMManager *manager);
+
+/* Connections */
+
+GSList *nm_manager_get_connections    (NMManager *manager, NMConnectionScope scope);
+
+NMConnection * nm_manager_get_connection_by_object_path (NMManager *manager,
+                                                         NMConnectionScope scope,
+                                                         const char *path);
+
+GPtrArray * nm_manager_get_active_connections_by_connection (NMManager *manager,
+                                                             NMConnection *connection);
+
+#endif /* NM_MANAGER_H */
diff --git a/src/nm-netlink-monitor.c b/src/nm-netlink-monitor.c
new file mode 100644
index 00000000..13b3ab98
--- /dev/null
+++ b/src/nm-netlink-monitor.c
@@ -0,0 +1,866 @@
+/* -*- 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 - 2010 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ * Copyright (C) 2005 Ray Strode
+ *
+ * Some code borrowed from HAL:  
+ *
+ * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
+ * Copyright (C) 2004 Novell, Inc.
+ */
+
+/* for struct ucred and LIBNL_NEEDS_ADDR_CACHING_WORKAROUND */
+#include <config.h>
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <linux/unistd.h>
+#include <unistd.h>
+#include <netlink/object-api.h>
+#include <netlink/route/addr.h>
+#include <netlink/route/rtnl.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-netlink-monitor.h"
+#include "nm-logging.h"
+
+#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))
+
+#define NM_NETLINK_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                           NM_TYPE_NETLINK_MONITOR, \
+                                           NMNetlinkMonitorPrivate))
+
+typedef struct {
+	/* Async event listener connection */
+	struct nl_handle *nlh_event;
+	GIOChannel *	  io_channel;
+	guint             event_id;
+
+	/* Sync/blocking request/response connection */
+	struct nl_handle *nlh_sync;
+	struct nl_cache * link_cache;
+
+	guint request_status_id;
+
+	GHashTable *subscriptions;
+} NMNetlinkMonitorPrivate;
+
+enum {
+	NOTIFICATION = 0,
+	CARRIER_ON,
+	CARRIER_OFF,
+	ERROR,
+	LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (NMNetlinkMonitor, nm_netlink_monitor, G_TYPE_OBJECT);
+
+static void
+link_msg_handler (struct nl_object *obj, void *arg)
+{
+	NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg);
+	GError *error;
+	struct rtnl_link *filter;
+	struct rtnl_link *link_obj;
+	guint flags;
+	guint ifidx;
+
+	filter = rtnl_link_alloc ();
+	if (!filter) {
+		error = g_error_new (NM_NETLINK_MONITOR_ERROR,
+		                     NM_NETLINK_MONITOR_ERROR_BAD_ALLOC,
+		                     _("error processing netlink message: %s"),
+		                     nl_geterror ());
+		g_signal_emit (self, signals[ERROR], 0, error);
+		g_error_free (error);
+		return;
+	}
+
+	/* Ensure it's a link object */
+	if (nl_object_match_filter (obj, OBJ_CAST (filter)) == 0) {
+		rtnl_link_put (filter);
+		return;
+	}
+
+	link_obj = (struct rtnl_link *) obj;
+	flags = rtnl_link_get_flags (link_obj);
+	ifidx = rtnl_link_get_ifindex (link_obj);
+
+	nm_log_dbg (LOGD_HW, "netlink link message: iface idx %d flags 0x%X", ifidx, flags);
+
+	/* IFF_LOWER_UP is the indicator of carrier status since kernel commit
+	 * b00055aacdb172c05067612278ba27265fcd05ce in 2.6.17.
+	 */
+	if (flags & IFF_LOWER_UP)
+		g_signal_emit (self, signals[CARRIER_ON], 0, ifidx);
+	else
+		g_signal_emit (self, signals[CARRIER_OFF], 0, ifidx);
+
+	rtnl_link_put (filter);
+}
+
+static int
+event_msg_recv (struct nl_msg *msg, void *arg)
+{
+	struct nl_handle *nlh = arg;
+	struct nlmsghdr *hdr = nlmsg_hdr (msg);
+	struct ucred *creds = nlmsg_get_creds (msg);
+	const struct sockaddr_nl *snl;
+	guint32 local_port;
+	gboolean accept_msg = FALSE;
+
+	/* Only messages sent from the kernel */
+	if (!creds || creds->uid != 0) {
+		nm_log_dbg (LOGD_HW, "ignoring netlink message from UID %d",
+		            creds ? creds->uid : -1);
+		return NL_SKIP;
+	}
+
+	snl = nlmsg_get_src (msg);
+	g_assert (snl);
+
+	/* Accept any messages from the kernel */
+	if (hdr->nlmsg_pid == 0 || snl->nl_pid == 0)
+		accept_msg = TRUE;
+
+	/* And any multicast message directed to our netlink PID, since multicast
+	 * currently requires CAP_ADMIN to use.
+	 */
+	local_port = nl_socket_get_local_port (nlh);
+	if ((hdr->nlmsg_pid == local_port) && snl->nl_groups)
+		accept_msg = TRUE;
+
+	if (accept_msg == FALSE) {
+		nm_log_dbg (LOGD_HW, "ignoring netlink message from PID %d (local PID %d, multicast %d)",
+		            hdr->nlmsg_pid,
+		            local_port,
+		            (hdr->nlmsg_flags & NLM_F_MULTI));
+		return NL_SKIP;
+	}
+
+	return NL_OK;
+}
+
+static int
+event_msg_ready (struct nl_msg *msg, void *arg)
+{
+	NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg);
+
+	/* By the time the message gets here we've already checked the sender
+	 * and we're sure it's safe to parse this message.
+	 */
+
+	/* Let clients handle generic messages */
+	g_signal_emit (self, signals[NOTIFICATION], 0, msg);
+
+	/* Parse carrier messages */
+	nl_msg_parse (msg, &link_msg_handler, self);
+
+	return NL_OK;
+}
+
+static gboolean
+event_handler (GIOChannel *channel,
+               GIOCondition io_condition,
+               gpointer user_data)
+{
+	NMNetlinkMonitor *self = (NMNetlinkMonitor *) user_data;
+	NMNetlinkMonitorPrivate *priv;
+	GError *error = NULL;
+
+	g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), TRUE);
+
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+	g_return_val_if_fail (priv->event_id > 0, TRUE);
+
+	if (io_condition & ERROR_CONDITIONS) {
+		const char *err_msg;
+		int err_code = 0;
+		socklen_t err_len = sizeof (err_code);
+
+		/* Grab error information */	 
+		if (getsockopt (g_io_channel_unix_get_fd (channel), 
+						SOL_SOCKET, SO_ERROR, (void *) &err_code, &err_len))
+			err_msg = strerror (err_code);
+		else
+			err_msg = _("error occurred while waiting for data on socket");
+
+		error = g_error_new (NM_NETLINK_MONITOR_ERROR,
+		                     NM_NETLINK_MONITOR_ERROR_WAITING_FOR_SOCKET_DATA,
+		                     "%s", err_msg);
+		g_signal_emit (self, signals[ERROR], 0, error);
+		g_error_free (error);
+		return TRUE;
+	} else if (io_condition & DISCONNECT_CONDITIONS)
+		return FALSE;
+
+	g_return_val_if_fail (!(io_condition & ~EVENT_CONDITIONS), FALSE);
+
+	/* Process the netlink messages */
+	if (nl_recvmsgs_default (priv->nlh_event) < 0) {
+		error = g_error_new (NM_NETLINK_MONITOR_ERROR,
+		                     NM_NETLINK_MONITOR_ERROR_PROCESSING_MESSAGE,
+		                     _("error processing netlink message: %s"),
+		                     nl_geterror ());
+		g_signal_emit (self, signals[ERROR], 0, error);
+		g_error_free (error);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+nlh_setup (struct nl_handle *nlh,
+           nl_recvmsg_msg_cb_t valid_func,
+           gpointer cb_data,
+           GError **error)
+{
+	nl_socket_modify_cb (nlh, NL_CB_MSG_IN, NL_CB_CUSTOM, event_msg_recv, cb_data);
+
+	if (valid_func)
+		nl_socket_modify_cb (nlh, NL_CB_VALID, NL_CB_CUSTOM, valid_func, cb_data);
+
+	if (nl_connect (nlh, NETLINK_ROUTE) < 0) {
+		g_set_error (error, NM_NETLINK_MONITOR_ERROR,
+		             NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT,
+		             _("unable to connect to netlink for monitoring link status: %s"),
+		             nl_geterror ());
+		return FALSE;
+	}
+
+	/* Enable unix socket peer credentials which we use for verifying that the
+	 * sender of the message is actually the kernel.
+	 */
+	if (nl_set_passcred (nlh, 1) < 0) {
+		g_set_error (error, NM_NETLINK_MONITOR_ERROR,
+		             NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT,
+		             _("unable to enable netlink handle credential passing: %s"),
+		             nl_geterror ());
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+event_connection_setup (NMNetlinkMonitor *self, GError **error)
+{
+	NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+	GError *channel_error = NULL;
+	GIOFlags channel_flags;
+	struct nl_cb *cb;
+	int fd;
+
+	g_return_val_if_fail (priv->io_channel == NULL, FALSE);
+
+	/* Set up the event listener connection */
+	cb = nl_cb_alloc (NL_CB_DEFAULT);
+	priv->nlh_event = nl_handle_alloc_cb (cb);
+	nl_cb_put (cb);
+	if (!priv->nlh_event) {
+		g_set_error (error, NM_NETLINK_MONITOR_ERROR,
+		             NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE,
+		             _("unable to allocate netlink handle for monitoring link status: %s"),
+		             nl_geterror ());
+		goto error;
+	}
+
+	if (!nlh_setup (priv->nlh_event, event_msg_ready, self, error))
+		goto error;
+
+	nl_disable_sequence_check (priv->nlh_event);
+
+	/* Subscribe to the LINK group for internal carrier signals */
+	if (!nm_netlink_monitor_subscribe (self, RTNLGRP_LINK, error))
+		goto error;
+
+	fd = nl_socket_get_fd (priv->nlh_event);
+	priv->io_channel = g_io_channel_unix_new (fd);
+
+	g_io_channel_set_encoding (priv->io_channel, NULL, &channel_error);
+	/* Encoding is NULL, so no conversion error can possibly occur */
+	g_assert (channel_error == NULL);
+
+	g_io_channel_set_close_on_unref (priv->io_channel, TRUE);
+	channel_flags = g_io_channel_get_flags (priv->io_channel);
+	channel_error = NULL;
+	g_io_channel_set_flags (priv->io_channel,
+	                        channel_flags | G_IO_FLAG_NONBLOCK,
+	                        &channel_error);
+	if (channel_error != NULL) {
+		g_propagate_error (error, channel_error);
+		goto error;
+	}
+
+	return TRUE;
+
+error:
+	if (priv->io_channel)
+		nm_netlink_monitor_close_connection (self);
+
+	if (priv->nlh_event) {
+		nl_handle_destroy (priv->nlh_event);
+		priv->nlh_event = NULL;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+sync_connection_setup (NMNetlinkMonitor *self, GError **error)
+{
+	NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+	struct nl_cb *cb;
+#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND
+	struct nl_cache *addr_cache;
+#endif
+
+	/* Set up the event listener connection */
+	cb = nl_cb_alloc (NL_CB_DEFAULT);
+	priv->nlh_sync = nl_handle_alloc_cb (cb);
+	nl_cb_put (cb);
+	if (!priv->nlh_sync) {
+		g_set_error (error, NM_NETLINK_MONITOR_ERROR,
+		             NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE,
+		             _("unable to allocate netlink handle for monitoring link status: %s"),
+		             nl_geterror ());
+		goto error;
+	}
+
+	if (!nlh_setup (priv->nlh_sync, NULL, self, error))
+		goto error;
+
+#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND
+	/* Work around apparent libnl bug; rtnl_addr requires that all
+	 * addresses have the "peer" attribute set in order to be compared
+	 * for equality, but this attribute is not normally set. As a
+	 * result, most addresses will not compare as equal even to
+	 * themselves, busting caching.
+	 */
+	addr_cache = rtnl_addr_alloc_cache (priv->nlh_sync);
+	nl_cache_get_ops (addr_cache)->co_obj_ops->oo_id_attrs &= ~0x80;
+	nl_cache_free (addr_cache);
+#endif
+
+	if ((priv->link_cache = rtnl_link_alloc_cache (priv->nlh_sync)) == NULL) {
+		g_set_error (error, NM_NETLINK_MONITOR_ERROR,
+		             NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_LINK_CACHE,
+		             _("unable to allocate netlink link cache for monitoring link status: %s"),
+		             nl_geterror ());
+		goto error;
+	}
+	nl_cache_mngt_provide (priv->link_cache);
+
+	return TRUE;
+
+error:
+	if (priv->link_cache) {
+		nl_cache_free (priv->link_cache);
+		priv->link_cache = NULL;
+	}
+
+	if (priv->nlh_sync) {
+		nl_handle_destroy (priv->nlh_sync);
+		priv->nlh_sync = NULL;
+	}
+
+	return FALSE;
+}
+
+gboolean
+nm_netlink_monitor_open_connection (NMNetlinkMonitor *self, GError **error)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
+
+	if (!event_connection_setup (self, error))
+		return FALSE;
+
+	if (!sync_connection_setup (self, error))
+		return FALSE;
+
+	return TRUE;
+}
+
+void
+nm_netlink_monitor_close_connection (NMNetlinkMonitor  *self)
+{
+	NMNetlinkMonitorPrivate *priv;
+
+	g_return_if_fail (NM_IS_NETLINK_MONITOR (self));
+
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+	g_return_if_fail (priv->io_channel != NULL);
+
+	if (priv->event_id)
+		nm_netlink_monitor_detach (self);
+
+	g_io_channel_shutdown (priv->io_channel,
+	                       TRUE /* flush pending data */,
+	                       NULL);
+	g_io_channel_unref (priv->io_channel);
+	priv->io_channel = NULL;
+}
+
+void
+nm_netlink_monitor_attach (NMNetlinkMonitor *self)
+{
+	NMNetlinkMonitorPrivate *priv;
+
+	g_return_if_fail (NM_IS_NETLINK_MONITOR (self));
+
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+	g_return_if_fail (priv->nlh_event != NULL);
+	g_return_if_fail (priv->event_id == 0);
+
+	priv->event_id = g_io_add_watch (priv->io_channel,
+	                                 (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS),
+	                                 event_handler, self);
+}
+
+void
+nm_netlink_monitor_detach (NMNetlinkMonitor *self)
+{
+	NMNetlinkMonitorPrivate *priv;
+
+	g_return_if_fail (NM_IS_NETLINK_MONITOR (self));
+
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+	g_return_if_fail (priv->event_id > 0);
+
+	g_source_remove (priv->event_id);
+	priv->event_id = 0;
+}
+
+static int
+get_subs (NMNetlinkMonitor *self, int group)
+{
+	NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	return GPOINTER_TO_INT (g_hash_table_lookup (priv->subscriptions,
+	                                             GINT_TO_POINTER (group)));
+}
+
+static void
+set_subs (NMNetlinkMonitor *self, int group, int new_subs)
+{
+	NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	g_hash_table_insert (priv->subscriptions,
+	                     GINT_TO_POINTER (group),
+	                     GINT_TO_POINTER (new_subs));
+}
+
+gboolean
+nm_netlink_monitor_subscribe (NMNetlinkMonitor *self, int group, GError **error)
+{
+	NMNetlinkMonitorPrivate *priv;
+	int subs;
+
+	g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
+
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	if (!priv->nlh_event) {
+		if (!nm_netlink_monitor_open_connection (self, error))
+			return FALSE;
+	}
+
+	subs = get_subs (self, group) + 1;
+	if (subs == 1) {
+		if (nl_socket_add_membership (priv->nlh_event, group) < 0) {
+			g_set_error (error, NM_NETLINK_MONITOR_ERROR,
+			             NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP,
+			             _("unable to join netlink group: %s"),
+			             nl_geterror ());
+			return FALSE;
+		}
+	}
+
+	/* Update # of subscriptions for this group */
+	set_subs (self, group, subs);
+
+	return TRUE;
+}
+
+void
+nm_netlink_monitor_unsubscribe (NMNetlinkMonitor *self, int group)
+{
+	NMNetlinkMonitorPrivate *priv;
+	int subs;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_NETLINK_MONITOR (self));
+
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+	g_return_if_fail (priv->nlh_event != NULL);
+
+	subs = get_subs (self, group) - 1;
+	if (subs == 0)
+		nl_socket_drop_membership (priv->nlh_event, group);
+
+	/* Update # of subscriptions for this group */
+	set_subs (self, group, subs);
+}
+
+/***************************************************************/
+
+gboolean
+nm_netlink_monitor_request_ip6_info (NMNetlinkMonitor *self, GError **error)
+{
+	NMNetlinkMonitorPrivate *priv;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
+
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	/* FIXME: nl_rtgen_request() gets the return value screwed up with
+	 * libnl-1.1; revisit this and return a proper error when we port to
+	 * a later libnl.
+	 */
+	nl_rtgen_request (priv->nlh_event, RTM_GETLINK, AF_INET6, NLM_F_DUMP);
+
+	return TRUE;
+}
+
+
+static gboolean
+deferred_emit_carrier_state (gpointer user_data)
+{
+	NMNetlinkMonitor *self = NM_NETLINK_MONITOR (user_data);
+	NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	priv->request_status_id = 0;
+
+	/* Update the link cache with latest state, and if there are no errors
+	 * emit the link states for all the interfaces in the cache.
+	 */
+	if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) {
+		nm_log_err (LOGD_HW, "error updating link cache: %s", nl_geterror ());
+	} else
+		nl_cache_foreach_filter (priv->link_cache, NULL, link_msg_handler, self);
+
+	return FALSE;
+}
+
+gboolean
+nm_netlink_monitor_request_status (NMNetlinkMonitor *self, GError **error)
+{
+	NMNetlinkMonitorPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
+
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	/* Schedule the carrier state emission */
+	if (!priv->request_status_id)
+		priv->request_status_id = g_idle_add (deferred_emit_carrier_state, self);
+
+	return TRUE;
+}
+
+typedef struct {
+	NMNetlinkMonitor *self;
+	struct rtnl_link *filter;
+	GError *error;
+	guint32 flags;
+} GetFlagsInfo;
+
+static void
+get_flags_sync_cb (struct nl_object *obj, void *arg)
+{
+	GetFlagsInfo *info = arg;
+
+	/* Ensure this cache item matches our filter */
+	if (nl_object_match_filter (obj, OBJ_CAST (info->filter)) != 0)
+		info->flags = rtnl_link_get_flags ((struct rtnl_link *) obj);
+}
+
+gboolean
+nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *self,
+                                   guint32 ifindex,
+                                   guint32 *ifflags,
+                                   GError **error)
+{
+	NMNetlinkMonitorPrivate *priv;
+	GetFlagsInfo info;
+	struct rtnl_link *filter;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
+	g_return_val_if_fail (ifflags != NULL, FALSE);
+
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	/* Update the link cache with the latest information */
+	if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) {
+		g_set_error (error,
+		             NM_NETLINK_MONITOR_ERROR,
+		             NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE,
+		             _("error updating link cache: %s"),
+		             nl_geterror ());
+		return FALSE;
+	}
+
+	/* HACK: Apparently to get it working we have to refill the cache twice;
+	 * otherwise some kernels (or maybe libnl?) only send a few of the
+	 * interfaces in the refill request.
+	 */
+	if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) {
+		g_set_error (error,
+		             NM_NETLINK_MONITOR_ERROR,
+		             NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE,
+		             _("error updating link cache: %s"),
+		             nl_geterror ());
+		return FALSE;
+	}
+
+	/* Set up the filter */
+	filter = rtnl_link_alloc ();
+	if (!filter) {
+		g_set_error (error,
+		             NM_NETLINK_MONITOR_ERROR,
+		             NM_NETLINK_MONITOR_ERROR_BAD_ALLOC,
+		             _("error processing netlink message: %s"),
+		             nl_geterror ());
+		return FALSE;
+	}
+	rtnl_link_set_ifindex (filter, ifindex);
+
+	memset (&info, 0, sizeof (info));
+	info.self = self;
+	info.filter = filter;
+	info.error = NULL;
+	nl_cache_foreach_filter (priv->link_cache, NULL, get_flags_sync_cb, &info);
+
+	rtnl_link_put (filter);
+
+	if (info.error) {
+		if (error)
+			*error = info.error;
+		else
+			g_error_free (info.error);
+		return FALSE;
+	} else
+		*ifflags = info.flags;
+
+	return TRUE; /* success */
+}
+
+/***************************************************************/
+
+struct nl_handle *
+nm_netlink_get_default_handle (void)
+{
+	NMNetlinkMonitor *self;
+	struct nl_handle *nlh;
+
+	self = nm_netlink_monitor_get ();
+	nlh = NM_NETLINK_MONITOR_GET_PRIVATE (self)->nlh_sync;
+	g_object_unref (self);
+
+	return nlh;
+}
+
+int
+nm_netlink_iface_to_index (const char *iface)
+{
+	NMNetlinkMonitor *self;
+	NMNetlinkMonitorPrivate *priv;
+	int idx;
+
+	g_return_val_if_fail (iface != NULL, -1);
+
+	self = nm_netlink_monitor_get ();
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	nl_cache_refill (priv->nlh_sync, priv->link_cache);
+	idx = rtnl_link_name2i (priv->link_cache, iface);
+	g_object_unref (self);
+
+	return idx;
+}
+
+#define MAX_IFACE_LEN 33
+char *
+nm_netlink_index_to_iface (int idx)
+{
+	NMNetlinkMonitor *self;
+	NMNetlinkMonitorPrivate *priv;
+	char *buf = NULL;
+
+	g_return_val_if_fail (idx >= 0, NULL);
+
+	self = nm_netlink_monitor_get ();
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	buf = g_malloc0 (MAX_IFACE_LEN);
+	g_assert (buf);
+
+	nl_cache_refill (priv->nlh_sync, priv->link_cache);
+	if (!rtnl_link_i2name (priv->link_cache, idx, buf, MAX_IFACE_LEN - 1)) {
+		g_free (buf);
+		buf = NULL;
+	}
+
+	g_object_unref (self);
+	return buf;
+}
+
+struct rtnl_link *
+nm_netlink_index_to_rtnl_link (int idx)
+{
+	NMNetlinkMonitor *self;
+	NMNetlinkMonitorPrivate *priv;
+	struct rtnl_link *ret = NULL;
+
+	if (idx <= 0)
+		return NULL;
+
+	self = nm_netlink_monitor_get ();
+	priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	nl_cache_refill (priv->nlh_sync, priv->link_cache);
+	ret = rtnl_link_get (priv->link_cache, idx);
+	g_object_unref (self);
+
+	return ret;
+}
+
+/***************************************************************/
+
+NMNetlinkMonitor *
+nm_netlink_monitor_get (void)
+{
+	static NMNetlinkMonitor *singleton = NULL;
+
+	if (!singleton)
+		singleton = NM_NETLINK_MONITOR (g_object_new (NM_TYPE_NETLINK_MONITOR, NULL));
+	else
+		g_object_ref (singleton);
+
+	return singleton;
+}
+
+static void
+nm_netlink_monitor_init (NMNetlinkMonitor *self)
+{
+	NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
+
+	priv->subscriptions = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (object);
+
+	if (priv->request_status_id)
+		g_source_remove (priv->request_status_id);
+
+	if (priv->io_channel)
+		nm_netlink_monitor_close_connection (NM_NETLINK_MONITOR (object));
+
+	if (priv->link_cache) {
+		nl_cache_free (priv->link_cache);
+		priv->link_cache = NULL;
+	}
+
+	if (priv->nlh_event) {
+		nl_handle_destroy (priv->nlh_event);
+		priv->nlh_event = NULL;
+	}
+
+	if (priv->nlh_sync) {
+		nl_handle_destroy (priv->nlh_sync);
+		priv->nlh_sync = NULL;
+	}
+
+	g_hash_table_destroy (priv->subscriptions);
+
+	G_OBJECT_CLASS (nm_netlink_monitor_parent_class)->finalize (object);
+}
+
+static void
+nm_netlink_monitor_class_init (NMNetlinkMonitorClass *monitor_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (monitor_class);
+
+	g_type_class_add_private (monitor_class, sizeof (NMNetlinkMonitorPrivate));
+
+	/* Virtual methods */
+	object_class->finalize = finalize;
+
+	/* Signals */
+	signals[NOTIFICATION] =
+		g_signal_new ("notification",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMNetlinkMonitorClass, notification),
+		              NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+		              G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	signals[CARRIER_ON] =
+		g_signal_new ("carrier-on",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_on),
+		              NULL, NULL, g_cclosure_marshal_VOID__INT,
+		              G_TYPE_NONE, 1, G_TYPE_INT);
+
+	signals[CARRIER_OFF] =
+		g_signal_new ("carrier-off",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_off),
+		              NULL, NULL, g_cclosure_marshal_VOID__INT,
+		              G_TYPE_NONE, 1, G_TYPE_INT);
+
+	signals[ERROR] =
+		g_signal_new ("error",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMNetlinkMonitorClass, error),
+		              NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+		              G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
+
+GQuark
+nm_netlink_monitor_error_quark (void)
+{
+	static GQuark error_quark = 0;
+
+	if (G_UNLIKELY (error_quark == 0))
+		error_quark = g_quark_from_static_string ("nm-netlink-monitor-error-quark");
+	return error_quark;
+}
+
diff --git a/src/nm-netlink-monitor.h b/src/nm-netlink-monitor.h
new file mode 100644
index 00000000..8b57a312
--- /dev/null
+++ b/src/nm-netlink-monitor.h
@@ -0,0 +1,99 @@
+/* -*- 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 - 2010 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ * Copyright (C) 2005 Ray Strode
+ */
+
+#ifndef NM_NETLINK_MONITOR_H
+#define NM_NETLINK_MONITOR_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#define NM_TYPE_NETLINK_MONITOR            (nm_netlink_monitor_get_type ())
+#define NM_NETLINK_MONITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitor))
+#define NM_NETLINK_MONITOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitorClass))
+#define NM_IS_NETLINK_MONITOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_NETLINK_MONITOR))
+#define NM_IS_NETLINK_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_NETLINK_MONITOR))
+#define NM_NETLINK_MONITOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_NETLINK_MONITOR, NMNetlinkMonitorClass))
+
+typedef enum {
+	NM_NETLINK_MONITOR_ERROR_GENERIC = 0,
+	NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE,
+	NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT,
+	NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP,
+	NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_LINK_CACHE,
+	NM_NETLINK_MONITOR_ERROR_PROCESSING_MESSAGE,
+	NM_NETLINK_MONITOR_ERROR_BAD_ALLOC,
+	NM_NETLINK_MONITOR_ERROR_WAITING_FOR_SOCKET_DATA,
+	NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE
+} NMNetlinkMonitorError;
+
+typedef struct {
+	GObject parent; 
+} NMNetlinkMonitor;
+
+typedef struct {
+	GObjectClass parent_class;
+
+	/* Signals */
+	void (*notification) (NMNetlinkMonitor *monitor, struct nl_msg *msg);
+	void (*carrier_on)   (NMNetlinkMonitor *monitor, int index);
+	void (*carrier_off)  (NMNetlinkMonitor *monitor, int index);
+	void (*error)        (NMNetlinkMonitor *monitor, GError *error);
+} NMNetlinkMonitorClass;
+
+
+#define NM_NETLINK_MONITOR_ERROR      (nm_netlink_monitor_error_quark ())
+GType  nm_netlink_monitor_get_type    (void) G_GNUC_CONST;
+GQuark nm_netlink_monitor_error_quark (void) G_GNUC_CONST;
+
+NMNetlinkMonitor *nm_netlink_monitor_get (void);
+
+gboolean          nm_netlink_monitor_open_connection  (NMNetlinkMonitor *monitor,
+                                                       GError **error);
+void              nm_netlink_monitor_close_connection (NMNetlinkMonitor *monitor);
+void              nm_netlink_monitor_attach           (NMNetlinkMonitor *monitor);
+void              nm_netlink_monitor_detach           (NMNetlinkMonitor *monitor);
+
+gboolean          nm_netlink_monitor_subscribe        (NMNetlinkMonitor *monitor,
+                                                       int group,
+                                                       GError **error);
+void              nm_netlink_monitor_unsubscribe      (NMNetlinkMonitor *monitor,
+                                                       int group);
+
+gboolean          nm_netlink_monitor_request_ip6_info (NMNetlinkMonitor *monitor,
+                                                       GError **error);
+
+gboolean          nm_netlink_monitor_request_status   (NMNetlinkMonitor *monitor,
+                                                       GError **error);
+gboolean          nm_netlink_monitor_get_flags_sync   (NMNetlinkMonitor *monitor,
+                                                       guint32 ifindex,
+                                                       guint32 *ifflags,
+                                                       GError **error);
+
+/* Generic utility functions */
+int               nm_netlink_iface_to_index     (const char *iface);
+char *            nm_netlink_index_to_iface     (int idx);
+struct rtnl_link *nm_netlink_index_to_rtnl_link (int idx);
+struct nl_handle *nm_netlink_get_default_handle (void);
+
+#endif  /* NM_NETLINK_MONITOR_H */
diff --git a/src/nm-policy-hostname.c b/src/nm-policy-hostname.c
new file mode 100644
index 00000000..a273a920
--- /dev/null
+++ b/src/nm-policy-hostname.c
@@ -0,0 +1,205 @@
+/* -*- 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) 2004 - 2010 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include "nm-logging.h"
+#include "nm-policy-hostname.h"
+#include "nm-policy-hosts.h"
+
+/************************************************************************/
+
+struct HostnameThread {
+	GThread *thread;
+
+	GMutex *lock;
+	gboolean dead;
+	int ret;
+
+	guint32 ip4_addr;
+	char hostname[NI_MAXHOST + 1];
+
+	HostnameThreadCallback callback;
+	gpointer user_data;
+};
+
+static gboolean
+hostname_thread_run_cb (gpointer user_data)
+{
+	HostnameThread *ht = (HostnameThread *) user_data;
+	const char *hostname = NULL;
+
+	if (strlen (ht->hostname))
+		hostname = ht->hostname;
+
+	(*ht->callback) (ht, ht->ret, hostname, ht->user_data);
+	return FALSE;
+}
+
+static gpointer
+hostname_thread_worker (gpointer data)
+{
+	HostnameThread *ht = (HostnameThread *) data;
+	struct sockaddr_in addr;
+	int i;
+
+	g_mutex_lock (ht->lock);
+	if (ht->dead) {
+		g_mutex_unlock (ht->lock);
+		return (gpointer) NULL;
+	}
+	g_mutex_unlock (ht->lock);
+
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = ht->ip4_addr;
+
+	ht->ret = getnameinfo ((struct sockaddr *) &addr, sizeof (struct sockaddr_in),
+	                       ht->hostname, NI_MAXHOST, NULL, 0,
+	                       NI_NAMEREQD);
+	if (ht->ret == 0) {
+		for (i = 0; i < strlen (ht->hostname); i++)
+			ht->hostname[i] = tolower (ht->hostname[i]);
+	}
+
+	/* Don't track the idle handler ID because by the time the g_idle_add()
+	 * returns the ID, the handler may already have run and freed the
+	 * HostnameThread.
+	 */
+	g_idle_add (hostname_thread_run_cb, ht);
+	return (gpointer) TRUE;
+}
+
+void
+hostname_thread_free (HostnameThread *ht)
+{
+	g_return_if_fail (ht != NULL);
+
+	g_mutex_free (ht->lock);
+	memset (ht, 0, sizeof (HostnameThread));
+	g_free (ht);
+}
+
+HostnameThread *
+hostname_thread_new (guint32 ip4_addr, HostnameThreadCallback callback, gpointer user_data)
+{
+	HostnameThread *ht;
+
+	ht = g_malloc0 (sizeof (HostnameThread));
+	g_assert (ht);
+
+	ht->lock = g_mutex_new ();
+	ht->callback = callback;
+	ht->user_data = user_data;
+	ht->ip4_addr = ip4_addr;
+
+	ht->thread = g_thread_create (hostname_thread_worker, ht, FALSE, NULL);
+	if (!ht->thread) {
+		hostname_thread_free (ht);
+		ht = NULL;
+	}
+
+	return ht;
+}
+
+void
+hostname_thread_kill (HostnameThread *ht)
+{
+	g_return_if_fail (ht != NULL);
+
+	g_mutex_lock (ht->lock);
+	ht->dead = TRUE;
+	g_mutex_unlock (ht->lock);
+}
+
+gboolean
+hostname_thread_is_dead (HostnameThread *ht)
+{
+	g_return_val_if_fail (ht != NULL, TRUE);
+
+	return ht->dead;
+}
+
+/************************************************************************/
+
+#define FALLBACK_HOSTNAME "localhost.localdomain"
+
+gboolean
+nm_policy_set_system_hostname (const char *new_hostname, const char *msg)
+{
+	char old_hostname[HOST_NAME_MAX + 1];
+	int ret = 0;
+	const char *name;
+	gboolean set_hostname = TRUE, changed = FALSE;
+
+	if (new_hostname)
+		g_warn_if_fail (strlen (new_hostname));
+
+	name = (new_hostname && strlen (new_hostname)) ? new_hostname : FALLBACK_HOSTNAME;
+
+	old_hostname[HOST_NAME_MAX] = '\0';
+	errno = 0;
+	ret = gethostname (old_hostname, HOST_NAME_MAX);
+	if (ret != 0) {
+		nm_log_warn (LOGD_DNS, "couldn't get the system hostname: (%d) %s",
+		             errno, strerror (errno));
+	} else {
+		/* Don't set the hostname if it isn't actually changing */
+		if (   (new_hostname && !strcmp (old_hostname, new_hostname))
+		    || (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME)))
+			set_hostname = FALSE;
+	}
+
+	if (set_hostname) {
+		nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg);
+		ret = sethostname (name, strlen (name));
+		if (ret != 0) {
+			nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s",
+			             name, errno, strerror (errno));
+			return FALSE;
+		}
+	}
+
+	/* But even if the hostname isn't changing, always try updating /etc/hosts
+	 * just in case the hostname changed while NM wasn't running; we need to
+	 * make sure that /etc/hosts has valid mappings for '127.0.0.1' and the
+	 * current system hostname.  If those exist,
+	 * nm_policy_hosts_update_etc_hosts() will just return and won't touch
+	 * /etc/hosts at all.
+	 */
+	if (!nm_policy_hosts_update_etc_hosts (name, FALLBACK_HOSTNAME, &changed)) {
+		/* error updating /etc/hosts; fallback to localhost.localdomain */
+		nm_log_info (LOGD_DNS, "Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)");
+		ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME));
+		if (ret != 0) {
+			nm_log_warn (LOGD_DNS, "couldn't set the fallback system hostname (%s): (%d) %s",
+			             FALLBACK_HOSTNAME, errno, strerror (errno));
+		}
+	}
+
+	return changed;
+}
+
diff --git a/src/nm-policy-hostname.h b/src/nm-policy-hostname.h
new file mode 100644
index 00000000..c59ca410
--- /dev/null
+++ b/src/nm-policy-hostname.h
@@ -0,0 +1,47 @@
+/* -*- 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) 2004 - 2010 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_POLICY_HOSTNAME_H
+#define NM_POLICY_HOSTNAME_H
+
+#include <glib.h>
+
+gboolean nm_policy_set_system_hostname (const char *new_hostname, const char *msg);
+
+
+typedef struct HostnameThread HostnameThread;
+
+typedef void (*HostnameThreadCallback) (HostnameThread *ht,
+                                        int error,
+                                        const char *hostname,
+                                        gpointer user_data);
+
+HostnameThread * hostname_thread_new (guint32 ip4_addr,
+                                      HostnameThreadCallback callback,
+                                      gpointer user_data);
+
+void             hostname_thread_free (HostnameThread *ht);
+
+gboolean         hostname_thread_is_dead (HostnameThread *ht);
+
+void             hostname_thread_kill (HostnameThread *ht);
+
+#endif /* NM_POLICY_HOSTNAME_H */
diff --git a/src/nm-policy-hosts.c b/src/nm-policy-hosts.c
new file mode 100644
index 00000000..7723c997
--- /dev/null
+++ b/src/nm-policy-hosts.c
@@ -0,0 +1,235 @@
+/* -*- 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) 2004 - 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <ctype.h>
+
+#include "nm-policy-hosts.h"
+#include "nm-logging.h"
+
+gboolean
+nm_policy_hosts_find_token (const char *line, const char *token)
+{
+	const char *start = line, *p = line;
+
+	g_return_val_if_fail (line != NULL, FALSE);
+	g_return_val_if_fail (token != NULL, FALSE);
+	g_return_val_if_fail (strlen (token) > 0, FALSE);
+
+	/* Walk through the line to find the next whitespace character */
+	while (p <= line + strlen (line)) {
+		if (isblank (*p) || (*p == '\0')) {
+			/* Token starts with 'start' and ends with 'end' */
+			if ((p > start) && *start && (p - start == strlen (token)) && !strncmp (start, token, (p - start)))
+				return TRUE; /* found */
+
+			/* not found; advance start and continue looking */
+			start = p + 1;
+		}
+		p++;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+is_local_mapping (const char *str, const char *hostname)
+{
+	return (   !strncmp (str, "127.0.0.1", strlen ("127.0.0.1"))
+	        && nm_policy_hosts_find_token (str, hostname ? hostname : "localhost"));
+}
+
+GString *
+nm_policy_get_etc_hosts (const char **lines,
+                         gsize existing_len,
+                         const char *hostname,
+                         const char *fallback_hostname,
+                         GError **error)
+{
+	GString *contents = NULL;
+	const char **line;
+	gboolean found_host_nonlocal = FALSE;
+	gboolean found_host = FALSE;
+	gboolean found_localhost = FALSE;
+	gboolean initial_comments = TRUE;
+	gboolean added = FALSE;
+
+	g_return_val_if_fail (lines != NULL, FALSE);
+	g_return_val_if_fail (hostname != NULL, FALSE);
+
+	/* /etc/hosts needs at least two things:
+	 *
+	 * 1) current hostname mapped to any address
+	 * 2) 'localhost' mapped to 127.0.0.1
+	 *
+	 * If both these conditions exist in /etc/hosts, we don't need to bother
+	 * updating the file.
+	 */
+
+	/* Look for the two cases from above */
+	for (line = lines; lines && *line; line++) {
+		if (strlen (*line) && (*line[0] != '#')) {
+			if (nm_policy_hosts_find_token (*line, hostname)) {
+				if (!is_local_mapping (*line, "localhost")) {
+					/* hostname is not on a 127.0.0.1 line or the line does not
+					 * contain 'localhost'.
+					 */
+					found_host_nonlocal = TRUE;
+				}
+				found_host = TRUE;
+			}
+
+			if (is_local_mapping (*line, "localhost")) {
+				/* a 127.0.0.1 line containing 'localhost' */
+				found_localhost = TRUE;
+			}
+		}
+
+		if (found_localhost && found_host)
+			return NULL;  /* No update required */
+	}
+
+	contents = g_string_sized_new (existing_len ? existing_len + 100 : 200);
+	if (!contents) {
+		g_set_error_literal (error, 0, 0, "not enough memory");
+		return NULL;
+	}
+
+	/* Construct the new hosts file; replace any 127.0.0.1 entry that is at the
+	 * beginning of the file or right after initial comments and contains
+	 * the string 'localhost'.  If there is no 127.0.0.1 entry at the beginning
+	 * or after initial comments that contains 'localhost', add one there
+	 * and ignore any other 127.0.0.1 entries that contain 'localhost'.
+	 */
+	for (line = lines, initial_comments = TRUE; lines && *line; line++) {
+		gboolean add_line = TRUE;
+
+		/* This is the first line after the initial comments */
+		if (strlen (*line) && initial_comments && (*line[0] != '#')) {
+			initial_comments = FALSE;
+
+			/* If some other line contained the hostname but not 'localhost',
+			 * make a simple localhost mapping and assume the user knows what
+			 * they are doing with their manual hostname entry.  Otherwise if
+			 * the hostname wasn't found somewhere else, add it to the localhost
+			 * mapping line to make sure it's mapped to something.
+			 */
+			if (found_host_nonlocal)
+				g_string_append (contents, "127.0.0.1");
+			else
+				g_string_append_printf (contents, "127.0.0.1\t%s", hostname);
+
+			if (strcmp (hostname, fallback_hostname)) {
+				g_string_append_printf (contents, "\t%s", fallback_hostname);
+				/* Don't add a standalone 'localhost.localdomain' 127 mapping */
+				if (is_local_mapping (*line, fallback_hostname))
+					add_line = FALSE;
+			}
+
+			g_string_append (contents, "\tlocalhost\n");
+			added = TRUE;
+
+			/* Don't add the original line if it is a 'localhost' mapping */
+			if (is_local_mapping (*line, "localhost"))
+				add_line = FALSE;
+		}
+
+		if (add_line) {
+			g_string_append (contents, *line);
+			/* Only append the new line if this isn't the last line in the file */
+			if (*(line+1))
+				g_string_append_c (contents, '\n');
+		}
+	}
+
+	/* Hmm, /etc/hosts was empty for some reason */
+	if (!added) {
+		g_string_append (contents, "# Do not remove the following line, or various programs\n");
+		g_string_append (contents, "# that require network functionality will fail.\n");
+		g_string_append_printf (contents, "127.0.0.1\t%s\tlocalhost\n", fallback_hostname);
+	}
+
+	return contents;
+}
+
+gboolean
+nm_policy_hosts_update_etc_hosts (const char *hostname,
+                                  const char *fallback_hostname,
+                                  gboolean *out_changed)
+{
+	char *contents = NULL;
+	char **lines = NULL;
+	GError *error = NULL;
+	GString *new_contents = NULL;
+	gsize contents_len = 0;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (hostname != NULL, FALSE);
+	g_return_val_if_fail (out_changed != NULL, FALSE);
+
+	if (!g_file_get_contents (SYSCONFDIR "/hosts", &contents, &contents_len, &error)) {
+		nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s",
+		             error ? error->code : 0,
+		             (error && error->message) ? error->message : "(unknown)");
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	/* Get the new /etc/hosts contents */
+	lines = g_strsplit_set (contents, "\n\r", 0);
+	new_contents = nm_policy_get_etc_hosts ((const char **) lines,
+	                                        contents_len,
+	                                        hostname,
+	                                        fallback_hostname,
+	                                        &error);
+	g_strfreev (lines);
+	g_free (contents);
+
+	if (new_contents) {
+		nm_log_info (LOGD_DNS, "Updating /etc/hosts with new system hostname");
+
+		g_clear_error (&error);
+		/* And actually update /etc/hosts */
+		if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) {
+			nm_log_warn (LOGD_DNS, "couldn't update " SYSCONFDIR "/hosts: (%d) %s",
+			             error ? error->code : 0,
+			             (error && error->message) ? error->message : "(unknown)");
+			g_clear_error (&error);
+		} else {
+			success = TRUE;
+			*out_changed = TRUE;
+		}
+
+		g_string_free (new_contents, TRUE);
+	} else if (!error) {
+		/* No change required */
+		success = TRUE;
+	} else {
+		nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s",
+		             error->code, error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+	}
+
+	return success;
+}
+
diff --git a/src/nm-policy-hosts.h b/src/nm-policy-hosts.h
new file mode 100644
index 00000000..0a77e667
--- /dev/null
+++ b/src/nm-policy-hosts.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2004 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_POLICY_HOSTS_H
+#define NM_POLICT_HOSTS_H
+
+#include <glib.h>
+
+gboolean nm_policy_hosts_update_etc_hosts (const char *hostname,
+                                           const char *fallback_hostname,
+                                           gboolean *out_changed);
+
+/* Only for testcases; don't use outside of nm-policy-hosts.c */
+gboolean nm_policy_hosts_find_token (const char *line, const char *token);
+
+GString *nm_policy_get_etc_hosts (const char **lines,
+                                  gsize existing_len,
+                                  const char *hostname,
+                                  const char *fallback_hostname,
+                                  GError **error);
+
+#endif /* NM_POLICY_HOSTS_H */
+
diff --git a/src/nm-policy.c b/src/nm-policy.c
new file mode 100644
index 00000000..455545b6
--- /dev/null
+++ b/src/nm-policy.c
@@ -0,0 +1,1138 @@
+/* -*- 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) 2004 - 2010 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <ctype.h>
+
+#include "nm-policy.h"
+#include "NetworkManagerUtils.h"
+#include "nm-wifi-ap.h"
+#include "nm-activation-request.h"
+#include "nm-logging.h"
+#include "nm-device-interface.h"
+#include "nm-device.h"
+#include "nm-device-wifi.h"
+#include "nm-device-ethernet.h"
+#include "nm-device-modem.h"
+#include "nm-dbus-manager.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-connection.h"
+#include "nm-system.h"
+#include "nm-named-manager.h"
+#include "nm-vpn-manager.h"
+#include "nm-policy-hosts.h"
+#include "nm-policy-hostname.h"
+
+struct NMPolicy {
+	NMManager *manager;
+	guint update_state_id;
+	GSList *pending_activation_checks;
+	GSList *signal_ids;
+	GSList *dev_signal_ids;
+
+	NMVPNManager *vpn_manager;
+	gulong vpn_activated_id;
+	gulong vpn_deactivated_id;
+
+	NMDevice *default_device4;
+	NMDevice *default_device6;
+
+	HostnameThread *lookup;
+
+	char *orig_hostname; /* hostname at NM start time */
+};
+
+#define INVALID_TAG "invalid"
+
+static const char *
+get_connection_id (NMConnection *connection)
+{
+	NMSettingConnection *s_con;
+
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	g_return_val_if_fail (s_con != NULL, NULL);
+
+	return nm_setting_connection_get_id (s_con);
+}
+
+static NMDevice *
+get_best_ip4_device (NMManager *manager, NMActRequest **out_req)
+{
+	GSList *devices, *iter;
+	NMDevice *best = NULL;
+	int best_prio = G_MAXINT;
+
+	g_return_val_if_fail (manager != NULL, NULL);
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+	g_return_val_if_fail (out_req != NULL, NULL);
+	g_return_val_if_fail (*out_req == NULL, NULL);
+
+	devices = nm_manager_get_devices (manager);
+	for (iter = devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *dev = NM_DEVICE (iter->data);
+		NMActRequest *req;
+		NMConnection *connection;
+		NMIP4Config *ip4_config;
+		NMSettingIP4Config *s_ip4;
+		int prio;
+		guint i;
+		gboolean can_default = FALSE;
+		const char *method = NULL;
+
+		if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED)
+			continue;
+
+		ip4_config = nm_device_get_ip4_config (dev);
+		if (!ip4_config)
+			continue;
+
+		req = nm_device_get_act_request (dev);
+		g_assert (req);
+		connection = nm_act_request_get_connection (req);
+		g_assert (connection);
+
+		/* Never set the default route through an IPv4LL-addressed device */
+		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+		if (s_ip4)
+			method = nm_setting_ip4_config_get_method (s_ip4);
+
+		if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
+			continue;
+
+		/* Make sure at least one of this device's IP addresses has a gateway */
+		for (i = 0; i < nm_ip4_config_get_num_addresses (ip4_config); i++) {
+			NMIP4Address *addr;
+
+			addr = nm_ip4_config_get_address (ip4_config, i);
+			if (nm_ip4_address_get_gateway (addr)) {
+				can_default = TRUE;
+				break;
+			}
+		}
+
+		if (!can_default && !NM_IS_DEVICE_MODEM (dev))
+			continue;
+
+		/* 'never-default' devices can't ever be the default */
+		if (s_ip4 && nm_setting_ip4_config_get_never_default (s_ip4))
+			continue;
+
+		prio = nm_device_get_priority (dev);
+		if (prio > 0 && prio < best_prio) {
+			best = dev;
+			best_prio = prio;
+			*out_req = req;
+		}
+	}
+
+	return best;
+}
+
+static NMDevice *
+get_best_ip6_device (NMManager *manager, NMActRequest **out_req)
+{
+	GSList *devices, *iter;
+	NMDevice *best = NULL;
+	int best_prio = G_MAXINT;
+
+	g_return_val_if_fail (manager != NULL, NULL);
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+	g_return_val_if_fail (out_req != NULL, NULL);
+	g_return_val_if_fail (*out_req == NULL, NULL);
+
+	devices = nm_manager_get_devices (manager);
+	for (iter = devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *dev = NM_DEVICE (iter->data);
+		NMActRequest *req;
+		NMConnection *connection;
+		NMIP6Config *ip6_config;
+		NMSettingIP6Config *s_ip6;
+		int prio;
+		guint i;
+		gboolean can_default = FALSE;
+		const char *method = NULL;
+
+		if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED)
+			continue;
+
+		ip6_config = nm_device_get_ip6_config (dev);
+		if (!ip6_config)
+			continue;
+
+		req = nm_device_get_act_request (dev);
+		g_assert (req);
+		connection = nm_act_request_get_connection (req);
+		g_assert (connection);
+
+		/* Never set the default route through an IPv4LL-addressed device */
+		s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+		if (s_ip6)
+			method = nm_setting_ip6_config_get_method (s_ip6);
+
+		if (method && !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
+			continue;
+
+		/* Make sure at least one of this device's IP addresses has a gateway */
+		for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
+			NMIP6Address *addr;
+
+			addr = nm_ip6_config_get_address (ip6_config, i);
+			if (nm_ip6_address_get_gateway (addr)) {
+				can_default = TRUE;
+				break;
+			}
+		}
+
+		if (!can_default && !NM_IS_DEVICE_MODEM (dev))
+			continue;
+
+		/* 'never-default' devices can't ever be the default */
+		if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6))
+			continue;
+
+		prio = nm_device_get_priority (dev);
+		if (prio > 0 && prio < best_prio) {
+			best = dev;
+			best_prio = prio;
+			*out_req = req;
+		}
+	}
+
+	return best;
+}
+
+static void
+_set_hostname (const char *new_hostname, const char *msg)
+{
+	if (nm_policy_set_system_hostname (new_hostname, msg))
+		nm_utils_call_dispatcher ("hostname", NULL, NULL, NULL);
+}
+
+static void
+lookup_callback (HostnameThread *thread,
+                 int result,
+                 const char *hostname,
+                 gpointer user_data)
+{
+	NMPolicy *policy = (NMPolicy *) user_data;
+	char *msg;
+
+	/* Update the hostname if the calling lookup thread is the in-progress one */
+	if (!hostname_thread_is_dead (thread) && (thread == policy->lookup)) {
+		policy->lookup = NULL;
+		if (!hostname) {
+			/* No valid IP4 config (!!); fall back to localhost.localdomain */
+			msg = g_strdup_printf ("address lookup failed: %d", result);
+			_set_hostname (NULL, msg);
+			g_free (msg);
+		} else
+			_set_hostname (hostname, "from address lookup");
+	}
+	hostname_thread_free (thread);
+}
+
+static void
+update_system_hostname (NMPolicy *policy, NMDevice *best)
+{
+	char *configured_hostname = NULL;
+	NMActRequest *best_req = NULL;
+	NMDHCP4Config *dhcp4_config;
+	NMIP4Config *ip4_config;
+	NMIP4Address *addr;
+
+	g_return_if_fail (policy != NULL);
+
+	if (policy->lookup) {
+		hostname_thread_kill (policy->lookup);
+		policy->lookup = NULL;
+	}
+
+	/* Hostname precedence order:
+	 *
+	 * 1) a configured hostname (from system-settings)
+	 * 2) automatic hostname from the default device's config (DHCP, VPN, etc)
+	 * 3) the original hostname when NM started
+	 * 4) reverse-DNS of the best device's IPv4 address
+	 *
+	 */
+
+	/* Try a persistent hostname first */
+	g_object_get (G_OBJECT (policy->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL);
+	if (configured_hostname) {
+		_set_hostname (configured_hostname, "from system configuration");
+		g_free (configured_hostname);
+		return;
+	}
+
+	/* Try automatically determined hostname from the best device's IP config */
+	if (!best)
+		best = get_best_ip4_device (policy->manager, &best_req);
+
+	if (!best) {
+		/* No best device; fall back to original hostname or if there wasn't
+		 * one, 'localhost.localdomain'
+		 */
+		_set_hostname (policy->orig_hostname, "no default device");
+		return;
+	}
+
+	/* Grab a hostname out of the device's DHCP4 config */
+	dhcp4_config = nm_device_get_dhcp4_config (best);
+	if (dhcp4_config) {
+		const char *dhcp4_hostname, *p;
+
+		p = dhcp4_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name");
+		if (dhcp4_hostname && strlen (dhcp4_hostname)) {
+			/* Sanity check */
+			while (*p) {
+				if (!isblank (*p++)) {
+					_set_hostname (dhcp4_hostname, "from DHCP");
+					return;
+				}
+			}
+			nm_log_warn (LOGD_DNS, "DHCP-provided hostname '%s' looks invalid; ignoring it",
+			             dhcp4_hostname);
+		}
+	}
+
+	/* If no automatically-configured hostname, try using the hostname from
+	 * when NM started up.
+	 */
+	if (policy->orig_hostname) {
+		_set_hostname (policy->orig_hostname, "from system startup");
+		return;
+	}
+
+	/* No configured hostname, no automatically determined hostname, and
+	 * no bootup hostname. Start reverse DNS of the current IP address.
+	 */
+	ip4_config = nm_device_get_ip4_config (best);
+	if (   !ip4_config
+	    || (nm_ip4_config_get_num_nameservers (ip4_config) == 0)
+	    || (nm_ip4_config_get_num_addresses (ip4_config) == 0)) {
+		/* No valid IP4 config (!!); fall back to localhost.localdomain */
+		_set_hostname (NULL, "no IPv4 config");
+		return;
+	}
+
+	addr = nm_ip4_config_get_address (ip4_config, 0);
+	g_assert (addr); /* checked for > 1 address above */
+
+	/* Start the hostname lookup thread */
+	policy->lookup = hostname_thread_new (nm_ip4_address_get_address (addr), lookup_callback, policy);
+	if (!policy->lookup) {
+		/* Fall back to 'localhost.localdomain' */
+		_set_hostname (NULL, "error starting hostname thread");
+	}
+}
+
+static void
+update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
+{
+	NMNamedIPConfigType dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
+	NMDevice *best = NULL;
+	NMActRequest *best_req = NULL;
+	NMNamedManager *named_mgr;
+	GSList *devices = NULL, *iter, *vpns;
+	NMIP4Config *ip4_config = NULL;
+	NMIP4Address *addr;
+	const char *ip_iface = NULL;
+	NMConnection *connection = NULL;
+	NMSettingConnection *s_con = NULL;
+	const char *connection_id;
+
+	best = get_best_ip4_device (policy->manager, &best_req);
+	if (!best)
+		goto out;
+	if (!force_update && (best == policy->default_device4))
+		goto out;
+
+	/* If a VPN connection is active, it is preferred */
+	vpns = nm_vpn_manager_get_active_connections (policy->vpn_manager);
+	for (iter = vpns; iter; iter = g_slist_next (iter)) {
+		NMVPNConnection *candidate = NM_VPN_CONNECTION (iter->data);
+		NMConnection *vpn_connection;
+		NMSettingIP4Config *s_ip4;
+		gboolean can_default = TRUE;
+		NMVPNConnectionState vpn_state;
+
+		/* If it's marked 'never-default', don't make it default */
+		vpn_connection = nm_vpn_connection_get_connection (candidate);
+		g_assert (vpn_connection);
+		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (vpn_connection, NM_TYPE_SETTING_IP4_CONFIG);
+		if (s_ip4 && nm_setting_ip4_config_get_never_default (s_ip4))
+			can_default = FALSE;
+
+		vpn_state = nm_vpn_connection_get_vpn_state (candidate);
+		if (can_default && (vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED)) {
+			NMIP4Config *parent_ip4;
+			NMDevice *parent;
+
+			ip_iface = nm_vpn_connection_get_ip_iface (candidate);
+			connection = nm_vpn_connection_get_connection (candidate);
+			ip4_config = nm_vpn_connection_get_ip4_config (candidate);
+			addr = nm_ip4_config_get_address (ip4_config, 0);
+
+			parent = nm_vpn_connection_get_parent_device (candidate);
+			parent_ip4 = nm_device_get_ip4_config (parent);
+
+			nm_system_replace_default_ip4_route_vpn (ip_iface,
+			                                         nm_ip4_address_get_gateway (addr),
+			                                         nm_vpn_connection_get_ip4_internal_gateway (candidate),
+			                                         nm_ip4_config_get_mss (ip4_config),
+			                                         nm_device_get_ip_iface (parent),
+			                                         nm_ip4_config_get_mss (parent_ip4));
+
+			dns_type = NM_NAMED_IP_CONFIG_TYPE_VPN;
+		}
+		g_object_unref (candidate);
+	}
+	g_slist_free (vpns);
+
+	/* The best device gets the default route if a VPN connection didn't */
+	if (!ip_iface || !ip4_config) {
+		connection = nm_act_request_get_connection (best_req);
+		ip_iface = nm_device_get_ip_iface (best);
+		ip4_config = nm_device_get_ip4_config (best);
+		g_assert (ip4_config);
+		addr = nm_ip4_config_get_address (ip4_config, 0);
+
+		nm_system_replace_default_ip4_route (ip_iface, nm_ip4_address_get_gateway (addr), nm_ip4_config_get_mss (ip4_config));
+
+		dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
+	}
+
+	if (!ip_iface || !ip4_config) {
+		nm_log_warn (LOGD_CORE, "couldn't determine IP interface (%p) or IPv4 config (%p)!",
+		             ip_iface, ip4_config);
+		goto out;
+	}
+
+	/* Update the default active connection.  Only mark the new default
+	 * active connection after setting default = FALSE on all other connections
+	 * first.  The order is important, we don't want two connections marked
+	 * default at the same time ever.
+	 */
+	devices = nm_manager_get_devices (policy->manager);
+	for (iter = devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *dev = NM_DEVICE (iter->data);
+		NMActRequest *req;
+
+		req = nm_device_get_act_request (dev);
+		if (req && (req != best_req))
+			nm_act_request_set_default (req, FALSE);
+	}
+
+	named_mgr = nm_named_manager_get ();
+	nm_named_manager_add_ip4_config (named_mgr, ip_iface, ip4_config, dns_type);
+	g_object_unref (named_mgr);
+
+	/* Now set new default active connection _after_ updating DNS info, so that
+	 * if the connection is shared dnsmasq picks up the right stuff.
+	 */
+	if (best_req)
+		nm_act_request_set_default (best_req, TRUE);
+
+	if (connection)
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+
+	connection_id = s_con ? nm_setting_connection_get_id (s_con) : NULL;
+	if (connection_id) {
+		nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv4 routing and DNS.", connection_id, ip_iface);
+	} else {
+		nm_log_info (LOGD_CORE, "Policy set (%s) as default for IPv4 routing and DNS.", ip_iface);
+	}
+
+out:
+	policy->default_device4 = best;
+}
+
+static void
+update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update)
+{
+	NMNamedIPConfigType dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
+	NMDevice *best = NULL;
+	NMActRequest *best_req = NULL;
+	NMNamedManager *named_mgr;
+	GSList *devices = NULL, *iter;
+#if NOT_YET
+	GSList *vpns;
+#endif
+	NMIP6Config *ip6_config = NULL;
+	NMIP6Address *addr;
+	const char *ip_iface = NULL;
+	NMConnection *connection = NULL;
+	NMSettingConnection *s_con = NULL;
+	const char *connection_id;
+
+	best = get_best_ip6_device (policy->manager, &best_req);
+	if (!best)
+		goto out;
+	if (!force_update && (best == policy->default_device6))
+		goto out;
+
+#if NOT_YET
+	/* If a VPN connection is active, it is preferred */
+	vpns = nm_vpn_manager_get_active_connections (policy->vpn_manager);
+	for (iter = vpns; iter; iter = g_slist_next (iter)) {
+		NMVPNConnection *candidate = NM_VPN_CONNECTION (iter->data);
+		NMConnection *vpn_connection;
+		NMSettingIP6Config *s_ip6;
+		gboolean can_default = TRUE;
+		NMVPNConnectionState vpn_state;
+
+		/* If it's marked 'never-default', don't make it default */
+		vpn_connection = nm_vpn_connection_get_connection (candidate);
+		g_assert (vpn_connection);
+		s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (vpn_connection, NM_TYPE_SETTING_IP6_CONFIG);
+		if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6))
+			can_default = FALSE;
+
+		vpn_state = nm_vpn_connection_get_vpn_state (candidate);
+		if (can_default && (vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED)) {
+			NMIP6Config *parent_ip6;
+			NMDevice *parent;
+
+			ip_iface = nm_vpn_connection_get_ip_iface (candidate);
+			connection = nm_vpn_connection_get_connection (candidate);
+			ip6_config = nm_vpn_connection_get_ip6_config (candidate);
+			addr = nm_ip6_config_get_address (ip6_config, 0);
+
+			parent = nm_vpn_connection_get_parent_device (candidate);
+			parent_ip6 = nm_device_get_ip6_config (parent);
+
+			nm_system_replace_default_ip6_route_vpn (ip_iface,
+			                                         nm_ip6_address_get_gateway (addr),
+			                                         nm_vpn_connection_get_ip4_internal_gateway (candidate),
+			                                         nm_ip6_config_get_mss (ip4_config),
+			                                         nm_device_get_ip_iface (parent),
+			                                         nm_ip6_config_get_mss (parent_ip4));
+
+			dns_type = NM_NAMED_IP_CONFIG_TYPE_VPN;
+		}
+		g_object_unref (candidate);
+	}
+	g_slist_free (vpns);
+#endif
+
+	/* The best device gets the default route if a VPN connection didn't */
+	if (!ip_iface || !ip6_config) {
+		connection = nm_act_request_get_connection (best_req);
+		ip_iface = nm_device_get_ip_iface (best);
+		ip6_config = nm_device_get_ip6_config (best);
+		g_assert (ip6_config);
+		addr = nm_ip6_config_get_address (ip6_config, 0);
+
+		nm_system_replace_default_ip6_route (ip_iface, nm_ip6_address_get_gateway (addr));
+
+		dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
+	}
+
+	if (!ip_iface || !ip6_config) {
+		nm_log_warn (LOGD_CORE, "couldn't determine IP interface (%p) or IPv6 config (%p)!",
+		             ip_iface, ip6_config);
+		goto out;
+	}
+
+	/* Update the default active connection.  Only mark the new default
+	 * active connection after setting default = FALSE on all other connections
+	 * first.  The order is important, we don't want two connections marked
+	 * default at the same time ever.
+	 */
+	devices = nm_manager_get_devices (policy->manager);
+	for (iter = devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *dev = NM_DEVICE (iter->data);
+		NMActRequest *req;
+
+		req = nm_device_get_act_request (dev);
+		if (req && (req != best_req))
+			nm_act_request_set_default6 (req, FALSE);
+	}
+
+	named_mgr = nm_named_manager_get ();
+	nm_named_manager_add_ip6_config (named_mgr, ip_iface, ip6_config, dns_type);
+	g_object_unref (named_mgr);
+
+	/* Now set new default active connection _after_ updating DNS info, so that
+	 * if the connection is shared dnsmasq picks up the right stuff.
+	 */
+	if (best_req)
+		nm_act_request_set_default6 (best_req, TRUE);
+
+	if (connection)
+		s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+
+	connection_id = s_con ? nm_setting_connection_get_id (s_con) : NULL;
+	if (connection_id) {
+		nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv6 routing and DNS.", connection_id, ip_iface);
+	} else {
+		nm_log_info (LOGD_CORE, "Policy set (%s) as default for IPv6 routing and DNS.", ip_iface);
+	}
+
+out:
+	policy->default_device6 = best;
+}
+
+static void
+update_routing_and_dns (NMPolicy *policy, gboolean force_update)
+{
+	update_ip4_routing_and_dns (policy, force_update);
+	update_ip6_routing_and_dns (policy, force_update);
+
+	/* Update the system hostname */
+	update_system_hostname (policy, policy->default_device4);
+}
+
+typedef struct {
+	NMPolicy *policy;
+	NMDevice *device;
+	guint id;
+} ActivateData;
+
+static gboolean
+auto_activate_device (gpointer user_data)
+{
+	ActivateData *data = (ActivateData *) user_data;
+	NMPolicy *policy;
+	NMConnection *best_connection;
+	char *specific_object = NULL;
+	GSList *connections, *iter;
+
+	g_assert (data);
+	policy = data->policy;
+
+	// FIXME: if a device is already activating (or activated) with a connection
+	// but another connection now overrides the current one for that device,
+	// deactivate the device and activate the new connection instead of just
+	// bailing if the device is already active
+	if (nm_device_get_act_request (data->device))
+		goto out;
+
+	/* System connections first, then user connections */
+	connections = nm_manager_get_connections (policy->manager, NM_CONNECTION_SCOPE_SYSTEM);
+	connections = g_slist_concat (connections, nm_manager_get_connections (policy->manager, NM_CONNECTION_SCOPE_USER));
+
+	/* Remove connections that are in the invalid list. */
+	iter = connections;
+	while (iter) {
+		NMConnection *iter_connection = NM_CONNECTION (iter->data);
+		GSList *next = g_slist_next (iter);
+
+		if (g_object_get_data (G_OBJECT (iter_connection), INVALID_TAG)) {
+			connections = g_slist_remove_link (connections, iter);
+			g_object_unref (iter_connection);
+			g_slist_free (iter);
+		}
+		iter = next;
+	}
+
+	best_connection = nm_device_get_best_auto_connection (data->device, connections, &specific_object);
+	if (best_connection) {
+		GError *error = NULL;
+		const char *device_path;
+
+		device_path = nm_device_get_path (data->device);
+		if (!nm_manager_activate_connection (policy->manager,
+		                                     best_connection,
+		                                     specific_object,
+		                                     device_path,
+		                                     FALSE,
+		                                     &error)) {
+			NMSettingConnection *s_con;
+
+			s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (best_connection, NM_TYPE_SETTING_CONNECTION));
+			g_assert (s_con);
+
+			nm_log_info (LOGD_DEVICE, "Connection '%s' auto-activation failed: (%d) %s",
+			             nm_setting_connection_get_id (s_con), error->code, error->message);
+			g_error_free (error);
+		}
+	}
+
+	g_slist_foreach (connections, (GFunc) g_object_unref, NULL);
+	g_slist_free (connections);
+
+ out:
+	/* Remove this call's handler ID */
+	policy->pending_activation_checks = g_slist_remove (policy->pending_activation_checks, data);
+	g_object_unref (data->device);
+	g_free (data);
+
+	return FALSE;
+}
+
+/*****************************************************************************/
+
+static void
+vpn_connection_activated (NMVPNManager *manager,
+                          NMVPNConnection *vpn,
+                          gpointer user_data)
+{
+	update_routing_and_dns ((NMPolicy *) user_data, TRUE);
+}
+
+static void
+vpn_connection_deactivated (NMVPNManager *manager,
+                            NMVPNConnection *vpn,
+                            NMVPNConnectionState state,
+                            NMVPNConnectionStateReason reason,
+                            gpointer user_data)
+{
+	update_routing_and_dns ((NMPolicy *) user_data, TRUE);
+}
+
+static void
+global_state_changed (NMManager *manager, NMState state, gpointer user_data)
+{
+}
+
+static void
+hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
+{
+	update_system_hostname ((NMPolicy *) user_data, NULL);
+}
+
+static void
+sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
+{
+	gboolean sleeping = FALSE, enabled = FALSE;
+	GSList *connections, *iter;
+
+	g_object_get (G_OBJECT (manager), NM_MANAGER_SLEEPING, &sleeping, NULL);
+	g_object_get (G_OBJECT (manager), NM_MANAGER_NETWORKING_ENABLED, &enabled, NULL);
+
+	/* Clear the invalid flag on all connections so they'll get retried on wakeup */
+	if (sleeping || !enabled) {
+		connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM);
+		connections = g_slist_concat (connections, nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER));
+		for (iter = connections; iter; iter = g_slist_next (iter))
+			g_object_set_data (G_OBJECT (iter->data), INVALID_TAG, NULL);
+		g_slist_free (connections);
+	}
+}
+
+static void
+schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds)
+{
+	ActivateData *data;
+	GSList *iter;
+	NMDeviceState state;
+
+	if (nm_manager_get_state (policy->manager) == NM_STATE_ASLEEP)
+		return;
+
+	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device));
+	if (state < NM_DEVICE_STATE_DISCONNECTED)
+		return;
+
+	if (!nm_device_autoconnect_allowed (device))
+		return;
+
+	for (iter = policy->pending_activation_checks; iter; iter = g_slist_next (iter)) {
+		/* Only one pending activation check at a time */
+		if (((ActivateData *) iter->data)->device == device)
+			return;
+	}
+
+	data = g_malloc0 (sizeof (ActivateData));
+	g_return_if_fail (data != NULL);
+
+	data->policy = policy;
+	data->device = g_object_ref (device);
+	data->id = delay_seconds ? g_timeout_add_seconds (delay_seconds, auto_activate_device, data) : g_idle_add (auto_activate_device, data);
+	policy->pending_activation_checks = g_slist_append (policy->pending_activation_checks, data);
+}
+
+static NMConnection *
+get_device_connection (NMDevice *device)
+{
+	NMActRequest *req;
+
+	req = nm_device_get_act_request (device);
+	if (!req)
+		return NULL;
+
+	return nm_act_request_get_connection (req);
+}
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMPolicy *policy = (NMPolicy *) user_data;
+	NMConnection *connection = get_device_connection (device);
+
+	switch (new_state) {
+	case NM_DEVICE_STATE_FAILED:
+		/* Mark the connection invalid if it failed during activation so that
+		 * it doesn't get automatically chosen over and over and over again.
+		 */
+		if (connection) {
+			gboolean fail = FALSE;
+
+			if (IS_ACTIVATING_STATE (old_state)) {
+				nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid.", get_connection_id (connection));
+				fail = TRUE;
+			} else if (   (old_state == NM_DEVICE_STATE_ACTIVATED)
+			         && (reason == NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED)) {
+				nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid because IP configuration expired.",
+				             get_connection_id (connection));
+				fail = TRUE;
+			}
+
+			if (fail) {
+				g_object_set_data (G_OBJECT (connection), INVALID_TAG, GUINT_TO_POINTER (TRUE));
+				nm_connection_clear_secrets (connection);
+			}
+		}
+		schedule_activate_check (policy, device, 3);
+		break;
+	case NM_DEVICE_STATE_ACTIVATED:
+		if (connection) {
+			/* Clear the invalid tag on the connection */
+			g_object_set_data (G_OBJECT (connection), INVALID_TAG, NULL);
+
+			/* And clear secrets so they will always be requested from the
+			 * settings service when the next connection is made.
+			 */
+			nm_connection_clear_secrets (connection);
+		}
+
+		update_routing_and_dns (policy, FALSE);
+		break;
+	case NM_DEVICE_STATE_UNMANAGED:
+	case NM_DEVICE_STATE_UNAVAILABLE:
+	case NM_DEVICE_STATE_DISCONNECTED:
+		update_routing_and_dns (policy, FALSE);
+		schedule_activate_check (policy, device, 0);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+device_ip_config_changed (NMDevice *device,
+                          GParamSpec *pspec,
+                          gpointer user_data)
+{
+	update_routing_and_dns ((NMPolicy *) user_data, TRUE);
+}
+
+static void
+wireless_networks_changed (NMDeviceWifi *device, NMAccessPoint *ap, gpointer user_data)
+{
+	schedule_activate_check ((NMPolicy *) user_data, NM_DEVICE (device), 0);
+}
+
+typedef struct {
+	gulong id;
+	NMDevice *device;
+} DeviceSignalID;
+
+static GSList *
+add_device_signal_id (GSList *list, gulong id, NMDevice *device)
+{
+	DeviceSignalID *data;
+
+	data = g_malloc0 (sizeof (DeviceSignalID));
+	if (!data)
+		return list;
+
+	data->id = id;
+	data->device = device;
+	return g_slist_append (list, data);
+}
+
+static void
+device_added (NMManager *manager, NMDevice *device, gpointer user_data)
+{
+	NMPolicy *policy = (NMPolicy *) user_data;
+	gulong id;
+
+	id = g_signal_connect (device, "state-changed",
+	                       G_CALLBACK (device_state_changed),
+	                       policy);
+	policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
+
+	id = g_signal_connect (device, "notify::" NM_DEVICE_INTERFACE_IP4_CONFIG,
+	                       G_CALLBACK (device_ip_config_changed),
+	                       policy);
+	policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
+
+	id = g_signal_connect (device, "notify::" NM_DEVICE_INTERFACE_IP6_CONFIG,
+	                       G_CALLBACK (device_ip_config_changed),
+	                       policy);
+	policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
+
+	if (NM_IS_DEVICE_WIFI (device)) {
+		id = g_signal_connect (device, "access-point-added",
+		                       G_CALLBACK (wireless_networks_changed),
+		                       policy);
+		policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
+
+		id = g_signal_connect (device, "access-point-removed",
+		                       G_CALLBACK (wireless_networks_changed),
+		                       policy);
+		policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
+	}
+}
+
+static void
+device_removed (NMManager *manager, NMDevice *device, gpointer user_data)
+{
+	NMPolicy *policy = (NMPolicy *) user_data;
+	GSList *iter;
+
+	/* Clear any idle callbacks for this device */
+	iter = policy->pending_activation_checks;
+	while (iter) {
+		ActivateData *data = (ActivateData *) iter->data;
+		GSList *next = g_slist_next (iter);
+
+		if (data->device == device) {
+			g_source_remove (data->id);
+			g_object_unref (data->device);
+			g_free (data);
+			policy->pending_activation_checks = g_slist_delete_link (policy->pending_activation_checks, iter);
+		}
+		iter = next;
+	}
+
+	/* Clear any signal handlers for this device */
+	iter = policy->dev_signal_ids;
+	while (iter) {
+		DeviceSignalID *data = (DeviceSignalID *) iter->data;
+		GSList *next = g_slist_next (iter);
+
+		if (data->device == device) {
+			g_signal_handler_disconnect (data->device, data->id);
+			g_free (data);
+			policy->dev_signal_ids = g_slist_delete_link (policy->dev_signal_ids, iter);
+		}
+		iter = next;
+	}
+
+	update_routing_and_dns (policy, FALSE);
+}
+
+static void
+schedule_activate_all (NMPolicy *policy)
+{
+	GSList *iter, *devices;
+
+	devices = nm_manager_get_devices (policy->manager);
+	for (iter = devices; iter; iter = g_slist_next (iter))
+		schedule_activate_check (policy, NM_DEVICE (iter->data), 0);
+}
+
+static void
+connections_added (NMManager *manager,
+                   NMConnectionScope scope,
+                   gpointer user_data)
+{
+	schedule_activate_all ((NMPolicy *) user_data);
+}
+
+static void
+connection_added (NMManager *manager,
+                  NMConnection *connection,
+                  NMConnectionScope scope,
+                  gpointer user_data)
+{
+	schedule_activate_all ((NMPolicy *) user_data);
+}
+
+static void
+connection_updated (NMManager *manager,
+                    NMConnection *connection,
+                    NMConnectionScope scope,
+                    gpointer user_data)
+{
+	/* Clear the invalid tag on the connection if it got updated. */
+	g_object_set_data (G_OBJECT (connection), INVALID_TAG, NULL);
+
+	schedule_activate_all ((NMPolicy *) user_data);
+}
+
+static void
+connection_removed (NMManager *manager,
+                    NMConnection *connection,
+                    NMConnectionScope scope,
+                    gpointer user_data)
+{
+	NMSettingConnection *s_con;
+	GPtrArray *list;
+	int i;
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	if (!s_con)
+		return;
+
+	list = nm_manager_get_active_connections_by_connection (manager, connection);
+	if (!list)
+		return;
+
+	for (i = 0; i < list->len; i++) {
+		char *path = g_ptr_array_index (list, i);
+		GError *error = NULL;
+
+		if (!nm_manager_deactivate_connection (manager, path, NM_DEVICE_STATE_REASON_CONNECTION_REMOVED, &error)) {
+			nm_log_warn (LOGD_DEVICE, "Connection '%s' disappeared, but error deactivating it: (%d) %s",
+			             nm_setting_connection_get_id (s_con), error->code, error->message);
+			g_error_free (error);
+		}
+		g_free (path);
+	}
+	g_ptr_array_free (list, TRUE);
+}
+
+NMPolicy *
+nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager)
+{
+	NMPolicy *policy;
+	static gboolean initialized = FALSE;
+	gulong id;
+	char hostname[HOST_NAME_MAX + 2];
+
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+	g_return_val_if_fail (initialized == FALSE, NULL);
+
+	policy = g_malloc0 (sizeof (NMPolicy));
+	policy->manager = g_object_ref (manager);
+	policy->update_state_id = 0;
+
+	/* Grab hostname on startup and use that if nothing provides one */
+	memset (hostname, 0, sizeof (hostname));
+	if (gethostname (&hostname[0], HOST_NAME_MAX) == 0) {
+		/* only cache it if it's a valid hostname */
+		if (strlen (hostname) && strcmp (hostname, "localhost") && strcmp (hostname, "localhost.localdomain"))
+			policy->orig_hostname = g_strdup (hostname);
+	}
+
+	policy->vpn_manager = g_object_ref (vpn_manager);
+	id = g_signal_connect (policy->vpn_manager, "connection-activated",
+	                       G_CALLBACK (vpn_connection_activated), policy);
+	policy->vpn_activated_id = id;
+	id = g_signal_connect (policy->vpn_manager, "connection-deactivated",
+	                       G_CALLBACK (vpn_connection_deactivated), policy);
+	policy->vpn_deactivated_id = id;
+
+	id = g_signal_connect (manager, "state-changed",
+	                       G_CALLBACK (global_state_changed), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	id = g_signal_connect (manager, "notify::" NM_MANAGER_HOSTNAME,
+	                       G_CALLBACK (hostname_changed), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	id = g_signal_connect (manager, "notify::" NM_MANAGER_SLEEPING,
+	                       G_CALLBACK (sleeping_changed), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	id = g_signal_connect (manager, "notify::" NM_MANAGER_NETWORKING_ENABLED,
+	                       G_CALLBACK (sleeping_changed), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	id = g_signal_connect (manager, "device-added",
+	                       G_CALLBACK (device_added), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	id = g_signal_connect (manager, "device-removed",
+	                       G_CALLBACK (device_removed), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	/* Large batch of connections added, manager doesn't want us to
+	 * process each one individually.
+	 */
+	id = g_signal_connect (manager, "connections-added",
+	                       G_CALLBACK (connections_added), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	/* Single connection added */
+	id = g_signal_connect (manager, "connection-added",
+	                       G_CALLBACK (connection_added), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	id = g_signal_connect (manager, "connection-updated",
+	                       G_CALLBACK (connection_updated), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	id = g_signal_connect (manager, "connection-removed",
+	                       G_CALLBACK (connection_removed), policy);
+	policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
+	return policy;
+}
+
+void
+nm_policy_destroy (NMPolicy *policy)
+{
+	GSList *iter;
+
+	g_return_if_fail (policy != NULL);
+
+	/* Tell any existing hostname lookup thread to die, it'll get cleaned up
+	 * by the lookup thread callback.
+	  */
+	if (policy->lookup) {
+		hostname_thread_kill (policy->lookup);
+		policy->lookup = NULL;
+	}
+
+	for (iter = policy->pending_activation_checks; iter; iter = g_slist_next (iter)) {
+		ActivateData *data = (ActivateData *) iter->data;
+
+		g_source_remove (data->id);
+		g_object_unref (data->device);
+		g_free (data);
+	}
+	g_slist_free (policy->pending_activation_checks);
+
+	g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_activated_id);
+	g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_deactivated_id);
+
+	for (iter = policy->signal_ids; iter; iter = g_slist_next (iter))
+		g_signal_handler_disconnect (policy->manager, (gulong) iter->data);
+	g_slist_free (policy->signal_ids);
+
+	for (iter = policy->dev_signal_ids; iter; iter = g_slist_next (iter)) {
+		DeviceSignalID *data = (DeviceSignalID *) iter->data;
+
+		g_signal_handler_disconnect (data->device, data->id);
+		g_free (data);
+	}
+	g_slist_free (policy->dev_signal_ids);
+
+	g_free (policy->orig_hostname);
+
+	g_object_unref (policy->manager);
+	g_free (policy);
+}
+
diff --git a/src/nm-policy.h b/src/nm-policy.h
new file mode 100644
index 00000000..7d99613c
--- /dev/null
+++ b/src/nm-policy.h
@@ -0,0 +1,36 @@
+/* -*- 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) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NETWORK_MANAGER_POLICY_H
+#define NETWORK_MANAGER_POLICY_H
+
+#include "NetworkManager.h"
+#include "nm-manager.h"
+#include "nm-vpn-manager.h"
+#include "nm-device.h"
+#include "nm-activation-request.h"
+
+typedef struct NMPolicy NMPolicy;
+
+NMPolicy *nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager);
+void nm_policy_destroy (NMPolicy *policy);
+
+#endif /* NETWORK_MANAGER_POLICY_H */
diff --git a/src/nm-properties-changed-signal.c b/src/nm-properties-changed-signal.c
new file mode 100644
index 00000000..4a9db8af
--- /dev/null
+++ b/src/nm-properties-changed-signal.c
@@ -0,0 +1,201 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <dbus/dbus-glib.h>
+#include "nm-properties-changed-signal.h"
+#include "nm-dbus-glib-types.h"
+
+#define NM_DBUS_PROPERTY_CHANGED "NM_DBUS_PROPERTY_CHANGED"
+
+typedef struct {
+	GHashTable *hash;
+	gulong signal_id;
+	guint idle_id;
+} PropertiesChangedInfo;
+
+static void
+destroy_value (gpointer data)
+{
+	GValue *val = (GValue *) data;
+
+	g_value_unset (val);
+	g_slice_free (GValue, val);
+}
+
+static PropertiesChangedInfo *
+properties_changed_info_new (void)
+{
+	PropertiesChangedInfo *info;
+
+	info = g_slice_new0 (PropertiesChangedInfo);
+	info->hash = g_hash_table_new_full (g_str_hash, g_str_equal, 
+								 (GDestroyNotify) g_free,
+								 destroy_value);
+	return info;
+}
+
+static void
+properties_changed_info_destroy (gpointer data)
+{
+	PropertiesChangedInfo *info = (PropertiesChangedInfo *) data;
+
+	if (info->idle_id)
+		g_source_remove (info->idle_id);
+
+	g_hash_table_destroy (info->hash);
+	g_slice_free (PropertiesChangedInfo, info);
+}
+
+#ifdef DEBUG
+static void
+add_to_string (gpointer key, gpointer value, gpointer user_data)
+{
+	char *buf = (char *) user_data;
+	GValue str_val = { 0, };
+
+	g_value_init (&str_val, G_TYPE_STRING);
+	if (!g_value_transform ((GValue *) value, &str_val)) {
+		if (G_VALUE_HOLDS_OBJECT (value)) {
+			GObject *obj = g_value_get_object (value);
+
+			if (g_value_get_object (value)) {
+				sprintf (buf + strlen (buf), "{%s: %p (%s)}, ",
+				         (const char *) key, obj, G_OBJECT_TYPE_NAME (obj));
+			} else {
+				sprintf (buf + strlen (buf), "{%s: %p}, ", (const char *) key, obj);
+			}
+		} else
+			sprintf (buf + strlen (buf), "{%s: <transform error>}, ", (const char *) key);
+	} else {
+		sprintf (buf + strlen (buf), "{%s: %s}, ", (const char *) key, g_value_get_string (&str_val));
+	}
+	g_value_unset (&str_val);
+}
+#endif
+
+static gboolean
+properties_changed (gpointer data)
+{
+	GObject *object = G_OBJECT (data);
+	PropertiesChangedInfo *info = (PropertiesChangedInfo *) g_object_get_data (object, NM_DBUS_PROPERTY_CHANGED);
+
+	g_assert (info);
+
+#ifdef DEBUG
+	{
+		char buf[2048] = { 0, };
+		g_hash_table_foreach (info->hash, add_to_string, &buf);
+		nm_log_dbg (LOGD_CORE, "%s -> %s", G_OBJECT_TYPE_NAME (object), buf);
+	}
+#endif
+
+	g_signal_emit (object, info->signal_id, 0, info->hash);
+	g_hash_table_remove_all (info->hash);
+
+	return FALSE;
+}
+
+static void
+idle_id_reset (gpointer data)
+{
+	GObject *object = G_OBJECT (data);
+	PropertiesChangedInfo *info = (PropertiesChangedInfo *) g_object_get_data (object, NM_DBUS_PROPERTY_CHANGED);
+
+	/* info is unset when the object is being destroyed */
+	if (info)
+		info->idle_id = 0;
+}
+
+static char*
+uscore_to_wincaps (const char *uscore)
+{
+	const char *p;
+	GString *str;
+	gboolean last_was_uscore;
+
+	last_was_uscore = TRUE;
+  
+	str = g_string_new (NULL);
+	p = uscore;
+	while (p && *p) {
+		if (*p == '-' || *p == '_')
+			last_was_uscore = TRUE;
+		else {
+			if (last_was_uscore) {
+				g_string_append_c (str, g_ascii_toupper (*p));
+				last_was_uscore = FALSE;
+			} else
+				g_string_append_c (str, *p);
+		}
+		++p;
+	}
+
+	return g_string_free (str, FALSE);
+}
+
+static void
+notify (GObject *object, GParamSpec *pspec)
+{
+	PropertiesChangedInfo *info;
+	GValue *value;
+
+	/* Ignore properties that shouldn't be exported */
+	if (pspec->flags & NM_PROPERTY_PARAM_NO_EXPORT)
+		return;
+
+	info = (PropertiesChangedInfo *) g_object_get_data (object, NM_DBUS_PROPERTY_CHANGED);
+	if (!info) {
+		info = properties_changed_info_new ();
+		g_object_set_data_full (object, NM_DBUS_PROPERTY_CHANGED, info, properties_changed_info_destroy);
+		info->signal_id = g_signal_lookup ("properties-changed", G_OBJECT_TYPE (object));
+		g_assert (info->signal_id);
+	}
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, pspec->value_type);
+	g_object_get_property (object, pspec->name, value);
+	g_hash_table_insert (info->hash, uscore_to_wincaps (pspec->name), value);
+
+	if (!info->idle_id)
+		info->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, properties_changed, object, idle_id_reset);
+}
+
+guint
+nm_properties_changed_signal_new (GObjectClass *object_class,
+						    guint class_offset)
+{
+	guint id;
+
+	object_class->notify = notify;
+
+	id = g_signal_new ("properties-changed",
+				    G_OBJECT_CLASS_TYPE (object_class),
+				    G_SIGNAL_RUN_FIRST,
+				    class_offset,
+				    NULL, NULL,
+				    g_cclosure_marshal_VOID__BOXED,
+				    G_TYPE_NONE, 1, DBUS_TYPE_G_MAP_OF_VARIANT);
+
+	return id;
+}
diff --git a/src/nm-properties-changed-signal.h b/src/nm-properties-changed-signal.h
new file mode 100644
index 00000000..b87e34e1
--- /dev/null
+++ b/src/nm-properties-changed-signal.h
@@ -0,0 +1,32 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef _NM_PROPERTIES_CHANGED_SIGNAL_H_
+#define _NM_PROPERTIES_CHANGED_SIGNAL_H_
+
+#include <glib-object.h>
+
+#define NM_PROPERTY_PARAM_NO_EXPORT    (1 << (0 + G_PARAM_USER_SHIFT))
+
+guint nm_properties_changed_signal_new (GObjectClass *object_class,
+								guint class_offset);
+
+#endif /* _NM_PROPERTIES_CHANGED_SIGNAL_H_ */
diff --git a/src/nm-rfkill.h b/src/nm-rfkill.h
new file mode 100644
index 00000000..d3cd7774
--- /dev/null
+++ b/src/nm-rfkill.h
@@ -0,0 +1,45 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_RFKILL_H
+#define NM_RFKILL_H
+
+typedef enum {
+	RFKILL_UNBLOCKED = 0,
+	RFKILL_SOFT_BLOCKED = 1,
+	RFKILL_HARD_BLOCKED = 2
+} RfKillState;
+
+typedef enum {
+	RFKILL_TYPE_WLAN = 0,
+	RFKILL_TYPE_WWAN = 1,
+	RFKILL_TYPE_WIMAX = 2,
+
+	/* UNKNOWN and MAX should always be 1 more than
+	 * the last rfkill type since RFKILL_TYPE_MAX is
+	 * used as an array size.
+	 */
+	RFKILL_TYPE_UNKNOWN = 3, /* KEEP LAST */
+	RFKILL_TYPE_MAX = RFKILL_TYPE_UNKNOWN
+} RfKillType;
+
+#endif  /* NM_RFKILL_H */
+
diff --git a/src/nm-secrets-provider-interface.c b/src/nm-secrets-provider-interface.c
new file mode 100644
index 00000000..47b8e57e
--- /dev/null
+++ b/src/nm-secrets-provider-interface.c
@@ -0,0 +1,224 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-marshal.h"
+#include "nm-secrets-provider-interface.h"
+
+#include <nm-setting-8021x.h>
+#include <nm-setting-wireless-security.h>
+#include "nm-logging.h"
+
+static void
+nm_secrets_provider_interface_init (gpointer g_iface)
+{
+	GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+	static gboolean initialized = FALSE;
+
+	if (initialized)
+		return;
+	initialized = TRUE;
+
+	/* Signals */
+	g_signal_new ("manager-get-secrets",
+	              iface_type,
+	              G_SIGNAL_RUN_LAST,
+	              G_STRUCT_OFFSET (NMSecretsProviderInterface, manager_get_secrets),
+	              NULL, NULL,
+	              _nm_marshal_BOOLEAN__POINTER_STRING_BOOLEAN_UINT_STRING_STRING,
+	              G_TYPE_BOOLEAN, 6,
+	              G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
+
+	g_signal_new ("manager-cancel-secrets",
+	              iface_type,
+	              G_SIGNAL_RUN_FIRST,
+	              G_STRUCT_OFFSET (NMSecretsProviderInterface, manager_cancel_secrets),
+	              NULL, NULL,
+	              g_cclosure_marshal_VOID__VOID,
+	              G_TYPE_NONE, 0);
+}
+
+
+GType
+nm_secrets_provider_interface_get_type (void)
+{
+	static GType interface_type = 0;
+
+	if (!interface_type) {
+		const GTypeInfo interface_info = {
+			sizeof (NMSecretsProviderInterface), /* class_size */
+			nm_secrets_provider_interface_init,   /* base_init */
+			NULL,		/* base_finalize */
+			NULL,
+			NULL,		/* class_finalize */
+			NULL,		/* class_data */
+			0,
+			0,              /* n_preallocs */
+			NULL
+		};
+
+		interface_type = g_type_register_static (G_TYPE_INTERFACE,
+		                                         "NMSecretsProviderInterface",
+		                                         &interface_info, 0);
+
+		g_type_interface_add_prerequisite (interface_type, G_TYPE_OBJECT);
+	}
+
+	return interface_type;
+}
+
+gboolean
+nm_secrets_provider_interface_get_secrets (NMSecretsProviderInterface *self,
+                                           NMConnection *connection,
+                                           const char *setting_name,
+                                           gboolean request_new,
+                                           RequestSecretsCaller caller,
+                                           const char *hint1,
+                                           const char *hint2)
+{
+	guint success = FALSE;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SECRETS_PROVIDER_INTERFACE (self), FALSE);
+	g_return_val_if_fail (connection != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (setting_name != NULL, FALSE);
+
+	nm_secrets_provider_interface_cancel_get_secrets (self);
+
+	g_signal_emit_by_name (self, "manager-get-secrets",
+	                       connection, setting_name, request_new, caller, hint1, hint2,
+	                       &success);
+	if (!success) {
+		nm_log_warn (LOGD_CORE, "failed to get connection secrets.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+nm_secrets_provider_interface_cancel_get_secrets (NMSecretsProviderInterface *self)
+{
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_SECRETS_PROVIDER_INTERFACE (self));
+
+	g_signal_emit_by_name (self, "manager-cancel-secrets");
+}
+
+
+static void
+add_one_key_to_list (gpointer key, gpointer data, gpointer user_data)
+{
+	GSList **list = (GSList **) user_data;
+
+	*list = g_slist_append (*list, key);
+}
+
+static gint
+settings_order_func (gconstpointer a, gconstpointer b)
+{
+	/* Just ensure the 802.1x setting gets processed _before_ the
+	 * wireless-security one.
+	 */
+
+	if (   !strcmp (a, NM_SETTING_802_1X_SETTING_NAME)
+	    && !strcmp (b, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME))
+		return -1;
+
+	if (   !strcmp (a, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME)
+	    && !strcmp (b, NM_SETTING_802_1X_SETTING_NAME))
+		return 1;
+
+	return 0;
+}
+
+void
+nm_secrets_provider_interface_get_secrets_result (NMSecretsProviderInterface *self,
+                                                  const char *setting_name,
+                                                  RequestSecretsCaller caller,
+                                                  GHashTable *settings,
+                                                  GError *error)
+{
+	GSList *keys = NULL, *iter;
+	GSList *updated = NULL;
+	GError *tmp_error = NULL;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_SECRETS_PROVIDER_INTERFACE (self));
+
+	if (error) {
+		NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->result (self,
+		                                                            setting_name,
+		                                                            caller,
+		                                                            NULL,
+		                                                            error);
+		return;
+	}
+
+	if (g_hash_table_size (settings) == 0) {
+		g_set_error (&tmp_error, 0, 0, "%s", "no secrets were received!");
+		NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->result (self,
+		                                                            setting_name,
+		                                                            caller,
+		                                                            NULL,
+		                                                            tmp_error);
+		g_clear_error (&tmp_error);
+		return;
+	}
+
+	g_hash_table_foreach (settings, add_one_key_to_list, &keys);
+	keys = g_slist_sort (keys, settings_order_func);
+	for (iter = keys; iter; iter = g_slist_next (iter)) {
+		GHashTable *hash;
+		const char *name = (const char *) iter->data;
+
+		hash = g_hash_table_lookup (settings, name);
+		if (!hash) {
+			nm_log_warn (LOGD_CORE, "couldn't get setting secrets for '%s'", name);
+			continue;
+		}
+
+		if (NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->update_setting (self, name, hash))
+			updated = g_slist_append (updated, (gpointer) setting_name);
+	}
+	g_slist_free (keys);
+
+	if (g_slist_length (updated)) {
+		NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->result (self,
+		                                                            setting_name,
+		                                                            caller,
+		                                                            updated,
+		                                                            NULL);
+	} else {
+		g_set_error (&tmp_error, 0, 0, "%s", "no secrets updated because no valid "
+		             "settings were received!");
+		NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE (self)->result (self,
+		                                                            setting_name,
+		                                                            caller,
+		                                                            NULL,
+		                                                            tmp_error);
+		g_clear_error (&tmp_error);
+	}
+
+	g_slist_free (updated);
+}
+
diff --git a/src/nm-secrets-provider-interface.h b/src/nm-secrets-provider-interface.h
new file mode 100644
index 00000000..3d9e08b1
--- /dev/null
+++ b/src/nm-secrets-provider-interface.h
@@ -0,0 +1,90 @@
+/* -*- 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) 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_SECRETS_PROVIDER_INTERFACE_H
+#define NM_SECRETS_PROVIDER_INTERFACE_H
+
+#include <glib-object.h>
+#include <nm-connection.h>
+
+typedef enum {
+	SECRETS_CALLER_NONE = 0,
+	SECRETS_CALLER_ETHERNET,
+	SECRETS_CALLER_WIFI,
+	SECRETS_CALLER_MOBILE_BROADBAND,
+	SECRETS_CALLER_PPP,
+	SECRETS_CALLER_VPN
+} RequestSecretsCaller;
+
+#define NM_TYPE_SECRETS_PROVIDER_INTERFACE               (nm_secrets_provider_interface_get_type ())
+#define NM_SECRETS_PROVIDER_INTERFACE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRETS_PROVIDER_INTERFACE, NMSecretsProviderInterface))
+#define NM_IS_SECRETS_PROVIDER_INTERFACE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SECRETS_PROVIDER_INTERFACE))
+#define NM_SECRETS_PROVIDER_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_SECRETS_PROVIDER_INTERFACE, NMSecretsProviderInterface))
+
+typedef struct _NMSecretsProviderInterface NMSecretsProviderInterface;
+
+struct _NMSecretsProviderInterface {
+	GTypeInterface g_iface;
+
+	/* Methods */
+	void (*result)             (NMSecretsProviderInterface *self,
+	                            const char *setting_name,
+	                            RequestSecretsCaller caller,
+	                            const GSList *updated,
+	                            GError *error);
+
+	gboolean (*update_setting) (NMSecretsProviderInterface *self,
+	                            const char *setting_name,
+	                            GHashTable *new);
+
+	/* Signals */
+	void (*manager_get_secrets)    (NMSecretsProviderInterface *self,
+	                                NMConnection *connection,
+	                                const char *setting_name,
+	                                gboolean request_new,
+	                                RequestSecretsCaller caller,
+	                                const char *hint1,
+	                                const char *hint2);
+
+	void (*manager_cancel_secrets) (NMSecretsProviderInterface *self);
+};
+
+GType nm_secrets_provider_interface_get_type (void);
+
+/* For callers */
+gboolean nm_secrets_provider_interface_get_secrets    (NMSecretsProviderInterface *self,
+                                                       NMConnection *connection,
+                                                       const char *setting_name,
+                                                       gboolean request_new,
+                                                       RequestSecretsCaller caller,
+                                                       const char *hint1,
+                                                       const char *hint2);
+
+void nm_secrets_provider_interface_cancel_get_secrets (NMSecretsProviderInterface *self);
+
+/* For NMManager */
+void nm_secrets_provider_interface_get_secrets_result (NMSecretsProviderInterface *self,
+                                                       const char *setting_name,
+                                                       RequestSecretsCaller caller,
+                                                       GHashTable *settings,
+                                                       GError *error);
+
+#endif /* NM_SECRETS_PROVIDER_INTERFACE_H */
+
diff --git a/src/nm-system.c b/src/nm-system.c
new file mode 100644
index 00000000..35aa36a1
--- /dev/null
+++ b/src/nm-system.c
@@ -0,0 +1,1347 @@
+/* -*- 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) 2004 - 2010 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ * Copyright (C) 1996 - 1997 Yoichi Hariguchi <yoichi@fore.com>
+ * Copyright (C) January, 1998 Sergei Viznyuk <sv@phystech.com>
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <net/route.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <glib.h>
+#include <ctype.h>
+#include <net/if.h>
+
+#include "nm-system.h"
+#include "nm-device.h"
+#include "nm-named-manager.h"
+#include "NetworkManagerUtils.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "nm-netlink-monitor.h"
+
+/* Because of a bug in libnl, rtnl.h should be included before route.h */
+#include <netlink/route/rtnl.h>
+
+#include <netlink/route/addr.h>
+#include <netlink/route/route.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/link.h>
+
+static void nm_system_device_set_priority (const char *iface,
+								   NMIP4Config *config,
+								   int priority);
+
+static gboolean
+ip4_dest_in_same_subnet (NMIP4Config *config, guint32 dest, guint32 dest_prefix)
+{
+	int num;
+	int i;
+
+	num = nm_ip4_config_get_num_addresses (config);
+	for (i = 0; i < num; i++) {
+		NMIP4Address *addr = nm_ip4_config_get_address (config, i);
+		guint32 prefix = nm_ip4_address_get_prefix (addr);
+		guint32 address = nm_ip4_address_get_address (addr);
+
+		if (prefix <= dest_prefix) {
+			guint32 masked_addr = ntohl(address) >> (32 - prefix);
+			guint32 masked_dest = ntohl(dest) >> (32 - prefix);
+
+			if (masked_addr == masked_dest)
+				return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static struct rtnl_route *
+create_route (int iface_idx, int mss)
+{
+	struct rtnl_route *route;
+
+	route = rtnl_route_alloc ();
+	if (route) {
+		rtnl_route_set_oif (route, iface_idx);
+
+		if (mss && rtnl_route_set_metric (route, RTAX_ADVMSS, mss) < 0) {
+			nm_log_warn (LOGD_DEVICE, "could not set mss");
+		}
+	} else
+		nm_log_err (LOGD_DEVICE, "could not allocate route");
+
+	return route;
+}
+
+static struct rtnl_route *
+nm_system_device_set_ip4_route (const char *iface, 
+                                guint32 ip4_dest,
+                                guint32 ip4_prefix,
+                                guint32 ip4_gateway,
+                                guint32 metric,
+                                int mss)
+{
+	struct nl_handle *nlh;
+	struct rtnl_route *route;
+	struct nl_addr *dest_addr;
+	struct nl_addr *gw_addr = NULL;
+	int err, iface_idx;
+
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, NULL);
+
+	iface_idx = nm_netlink_iface_to_index (iface);
+	g_return_val_if_fail (iface_idx >= 0, NULL);
+
+	route = create_route (iface_idx, mss);
+	g_return_val_if_fail (route != NULL, NULL);
+
+	/* Destination */
+	dest_addr = nl_addr_build (AF_INET, &ip4_dest, sizeof (ip4_dest));
+	g_return_val_if_fail (dest_addr != NULL, NULL);
+	nl_addr_set_prefixlen (dest_addr, (int) ip4_prefix);
+
+	rtnl_route_set_dst (route, dest_addr);
+	nl_addr_put (dest_addr);
+
+	/* Gateway */
+	if (ip4_gateway) {
+		gw_addr = nl_addr_build (AF_INET, &ip4_gateway, sizeof (ip4_gateway));
+		if (gw_addr) {
+			rtnl_route_set_gateway (route, gw_addr);
+			rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
+		} else {
+			nm_log_err (LOGD_DEVICE | LOGD_IP4, "Invalid gateway 0x%X", ip4_gateway);
+			rtnl_route_put (route);
+			return NULL;
+		}
+	}
+
+	/* Metric */
+	if (metric)
+		rtnl_route_set_prio (route, metric);
+
+	/* Add the route */
+	err = rtnl_route_add (nlh, route, 0);
+	if (err == -ESRCH && ip4_gateway) {
+		/* Gateway might be over a bridge; try adding a route to gateway first */
+		struct rtnl_route *route2;
+
+		route2 = create_route (iface_idx, mss);
+		if (route2) {
+			/* Add route to gateway over bridge */
+			rtnl_route_set_dst (route2, gw_addr);
+			err = rtnl_route_add (nlh, route2, 0);
+			if (!err) {
+				/* Try adding the route again */
+				err = rtnl_route_add (nlh, route, 0);
+				if (err)
+					rtnl_route_del (nlh, route2, 0);
+			}
+			rtnl_route_put (route2);
+		}
+	}
+
+	if (gw_addr)
+		nl_addr_put (gw_addr);
+
+	if (err) {
+		nm_log_err (LOGD_DEVICE | LOGD_IP4,
+		            "(%s): failed to set IPv4 route: %s",
+		            iface, nl_geterror ());
+		rtnl_route_put (route);
+		route = NULL;
+	}
+
+	return route;
+}
+
+static gboolean
+sync_addresses (const char *iface, int ifindex, int family,
+				struct rtnl_addr **addrs, int num_addrs)
+{
+	struct nl_handle *nlh;
+	struct nl_cache *addr_cache;
+	struct rtnl_addr *filter_addr, *match_addr;
+	struct nl_object *match;
+	struct nl_addr *nladdr;
+	int i, err;
+	guint32 log_domain = (family == AF_INET) ? LOGD_IP4 : LOGD_IP6;
+	char buf[INET6_ADDRSTRLEN + 1];
+
+	log_domain |= LOGD_DEVICE;
+
+	nlh = nm_netlink_get_default_handle ();
+	if (!nlh)
+		return FALSE;
+
+	addr_cache = rtnl_addr_alloc_cache (nlh);
+	if (!addr_cache)
+		return FALSE;
+
+	filter_addr = rtnl_addr_alloc ();
+	if (!filter_addr) {
+		nl_cache_free (addr_cache);
+		return FALSE;
+	}
+	rtnl_addr_set_ifindex (filter_addr, ifindex);
+	if (family)
+		rtnl_addr_set_family (filter_addr, family);
+
+	nm_log_dbg (log_domain, "(%s): syncing addresses (family %d)", iface, family);
+
+	/* Walk through the cache, comparing the addresses already on
+	 * the interface to the addresses in addrs.
+	 */
+	for (match = nl_cache_get_first (addr_cache); match; match = nl_cache_get_next (match)) {
+		gboolean buf_valid = FALSE;
+		match_addr = (struct rtnl_addr *) match;
+
+		/* Skip addresses not on our interface */
+		if (!nl_object_match_filter (match, (struct nl_object *) filter_addr))
+			continue;
+
+		if (addrs) {
+			for (i = 0; i < num_addrs; i++) {
+				if (addrs[i] && nl_object_identical (match, (struct nl_object *) addrs[i]))
+					break;
+			}
+
+			if (addrs[i]) {
+				/* match == addrs[i], so remove it from addrs so we don't
+				 * try to add it to the interface again below.
+				 */
+				rtnl_addr_put (addrs[i]);
+				addrs[i] = NULL;
+				continue;
+			}
+		}
+
+		nladdr = rtnl_addr_get_local (match_addr);
+
+		/* Don't delete IPv6 link-local addresses; they don't belong to NM */
+		if (rtnl_addr_get_family (match_addr) == AF_INET6) {
+			struct in6_addr *tmp;
+
+			if (rtnl_addr_get_scope (match_addr) == RT_SCOPE_LINK) {
+				nm_log_dbg (log_domain, "(%s): ignoring IPv6 link-local address", iface);
+				continue;
+			}
+
+			tmp = nl_addr_get_binary_addr (nladdr);
+			if (inet_ntop (AF_INET6, tmp, buf, sizeof (buf)))
+				buf_valid = TRUE;
+		} else if (rtnl_addr_get_family (match_addr) == AF_INET) {
+			struct in_addr *tmp;
+
+			tmp = nl_addr_get_binary_addr (nladdr);
+			if (inet_ntop (AF_INET, tmp, buf, sizeof (buf)))
+				buf_valid = TRUE;
+		}
+
+		if (buf_valid) {
+			nm_log_dbg (log_domain, "(%s): removing address '%s/%d'",
+			            iface, buf, nl_addr_get_prefixlen (nladdr));
+		}
+
+		/* Otherwise, match_addr should be removed from the interface. */
+		err = rtnl_addr_delete (nlh, match_addr, 0);
+		if (err < 0) {
+			nm_log_err (log_domain, "(%s): error %d returned from rtnl_addr_delete(): %s",
+						iface, err, nl_geterror ());
+		}
+	}
+
+	rtnl_addr_put (filter_addr);
+	nl_cache_free (addr_cache);
+
+	/* Now add the remaining new addresses */
+	for (i = 0; i < num_addrs; i++) {
+		struct in6_addr *in6tmp;
+		struct in_addr *in4tmp;
+		gboolean buf_valid = FALSE;
+
+		if (!addrs[i])
+			continue;
+
+		nladdr = rtnl_addr_get_local (addrs[i]);
+		if (rtnl_addr_get_family (addrs[i]) == AF_INET6) {
+			in6tmp = nl_addr_get_binary_addr (nladdr);
+			if (inet_ntop (AF_INET6, in6tmp, buf, sizeof (buf)))
+				buf_valid = TRUE;
+		} else if (rtnl_addr_get_family (addrs[i]) == AF_INET) {
+			in4tmp = nl_addr_get_binary_addr (nladdr);
+			if (inet_ntop (AF_INET, in4tmp, buf, sizeof (buf)))
+				buf_valid = TRUE;
+		}
+
+		if (buf_valid) {
+			nm_log_dbg (log_domain, "(%s): adding address '%s/%d'",
+			            iface, buf, nl_addr_get_prefixlen (nladdr));
+		}
+
+		err = rtnl_addr_add (nlh, addrs[i], 0);
+		if (err < 0) {
+			nm_log_err (log_domain,
+			            "(%s): error %d returned from rtnl_addr_add():\n%s",
+						iface, err, nl_geterror ());
+		}
+
+		rtnl_addr_put (addrs[i]);
+	}
+	g_free (addrs);
+
+	return TRUE;
+}
+
+static gboolean
+add_ip4_addresses (NMIP4Config *config, const char *iface)
+{
+	int num_addrs, i, iface_idx;
+	guint32 flags = 0;
+	gboolean did_gw = FALSE;
+	struct rtnl_addr **addrs;
+
+	iface_idx = nm_netlink_iface_to_index (iface);
+
+	num_addrs = nm_ip4_config_get_num_addresses (config);
+	addrs = g_new0 (struct rtnl_addr *, num_addrs + 1);
+
+	for (i = 0; i < num_addrs; i++) {
+		NMIP4Address *addr;
+
+		addr = nm_ip4_config_get_address (config, i);
+		g_assert (addr);
+
+		flags = NM_RTNL_ADDR_DEFAULT;
+		if (nm_ip4_address_get_gateway (addr) && !did_gw) {
+			if (nm_ip4_config_get_ptp_address (config))
+				flags |= NM_RTNL_ADDR_PTP_ADDR;
+			did_gw = TRUE;
+		}
+
+		addrs[i] = nm_ip4_config_to_rtnl_addr (config, i, flags);
+		if (!addrs[i]) {
+			nm_log_warn (LOGD_DEVICE | LOGD_IP4,
+			             "(%s): couldn't create rtnl address!",
+			             iface);
+			continue;
+		}
+		rtnl_addr_set_ifindex (addrs[i], iface_idx);
+	}
+
+	return sync_addresses (iface, iface_idx, AF_INET, addrs, num_addrs);
+}
+
+struct rtnl_route *
+nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device, NMIP4Config *vpn_config)
+{
+	NMIP4Config *parent_config;
+	guint32 parent_gw = 0, parent_prefix = 0, vpn_gw = 0, i;
+	NMIP4Address *tmp;
+	struct rtnl_route *route = NULL;
+
+	g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL);
+
+	/* Set up a route to the VPN gateway's public IP address through the default
+	 * network device if the VPN gateway is on a different subnet.
+	 */
+
+	parent_config = nm_device_get_ip4_config (parent_device);
+	g_return_val_if_fail (parent_config != NULL, NULL);
+
+	for (i = 0; i < nm_ip4_config_get_num_addresses (parent_config); i++) {
+		tmp = nm_ip4_config_get_address (parent_config, i);
+		if (nm_ip4_address_get_gateway (tmp)) {
+			parent_gw = nm_ip4_address_get_gateway (tmp);
+			parent_prefix = nm_ip4_address_get_prefix (tmp);
+			break;
+		}
+	}
+
+	for (i = 0; i < nm_ip4_config_get_num_addresses (vpn_config); i++) {
+		tmp = nm_ip4_config_get_address (vpn_config, i);
+		if (nm_ip4_address_get_gateway (tmp)) {
+			vpn_gw = nm_ip4_address_get_gateway (tmp);
+			break;
+		}
+	}
+
+	if (!parent_gw || !vpn_gw)
+		return NULL;
+
+	/* If the VPN gateway is in the same subnet as one of the parent device's
+	 * IP addresses, don't add the host route to it, but a route through the
+	 * parent device.
+	 */
+	if (ip4_dest_in_same_subnet (parent_config, vpn_gw, parent_prefix)) {
+		route = nm_system_device_set_ip4_route (nm_device_get_ip_iface (parent_device),
+		                                        vpn_gw, 32, 0, 0, nm_ip4_config_get_mss (parent_config));
+	} else {
+		route = nm_system_device_set_ip4_route (nm_device_get_ip_iface (parent_device),
+		                                        vpn_gw, 32, parent_gw, 0, nm_ip4_config_get_mss (parent_config));
+	}
+
+	return route;
+}
+
+/*
+ * nm_system_apply_ip4_config
+ *
+ * Set IPv4 configuration of the device from an NMIP4Config object.
+ *
+ */
+gboolean
+nm_system_apply_ip4_config (const char *iface,
+                            NMIP4Config *config,
+                            int priority,
+                            NMIP4ConfigCompareFlags flags)
+{
+	int i;
+
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (config != NULL, FALSE);
+
+	if (flags & NM_IP4_COMPARE_FLAG_ADDRESSES) {
+		if (!add_ip4_addresses (config, iface))
+			return FALSE;
+		sleep (1);
+	}
+
+	if (flags & NM_IP4_COMPARE_FLAG_ROUTES) {
+		for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) {
+			NMIP4Route *route = nm_ip4_config_get_route (config, i);
+			struct rtnl_route *tmp;
+
+			/* Don't add the route if it's more specific than one of the subnets
+			 * the device already has an IP address on.
+			 */
+			if (ip4_dest_in_same_subnet (config,
+			                             nm_ip4_route_get_dest (route),
+			                             nm_ip4_route_get_prefix (route)))
+				continue;
+
+			/* Don't add the route if it doesn't have a gateway and the connection
+			 * is never supposed to be the default connection.
+			 */
+			if (   nm_ip4_config_get_never_default (config)
+			    && nm_ip4_route_get_dest (route) == 0)
+				continue;
+
+			tmp = nm_system_device_set_ip4_route (iface,
+			                                      nm_ip4_route_get_dest (route),
+			                                      nm_ip4_route_get_prefix (route),
+			                                      nm_ip4_route_get_next_hop (route),
+			                                      nm_ip4_route_get_metric (route),
+			                                      nm_ip4_config_get_mss (config));
+			rtnl_route_put (tmp);
+		}
+	}
+
+	if (flags & NM_IP4_COMPARE_FLAG_MTU) {
+		if (nm_ip4_config_get_mtu (config))
+			nm_system_device_set_mtu (iface, nm_ip4_config_get_mtu (config));
+	}
+
+	if (priority > 0)
+		nm_system_device_set_priority (iface, config, priority);
+
+	return TRUE;
+}
+
+static struct rtnl_route *
+nm_system_device_set_ip6_route (const char *iface,
+                                const struct in6_addr *ip6_dest,
+                                guint32 ip6_prefix,
+                                const struct in6_addr *ip6_gateway,
+                                guint32 metric,
+                                int mss)
+{
+	struct nl_handle *nlh;
+	struct rtnl_route *route;
+	struct nl_addr *dest_addr;
+	struct nl_addr *gw_addr = NULL;
+	int err, iface_idx;
+
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, NULL);
+
+	iface_idx = nm_netlink_iface_to_index (iface);
+	g_return_val_if_fail (iface_idx >= 0, NULL);
+
+	route = create_route (iface_idx, mss);
+	g_return_val_if_fail (route != NULL, NULL);
+
+	/* Destination */
+	dest_addr = nl_addr_build (AF_INET6, (struct in6_addr *) ip6_dest, sizeof (*ip6_dest));
+	g_return_val_if_fail (dest_addr != NULL, NULL);
+	nl_addr_set_prefixlen (dest_addr, (int) ip6_prefix);
+
+	rtnl_route_set_dst (route, dest_addr);
+	nl_addr_put (dest_addr);
+
+	/* Gateway */
+	if (ip6_gateway && !IN6_IS_ADDR_UNSPECIFIED (ip6_gateway)) {
+		gw_addr = nl_addr_build (AF_INET6, (struct in6_addr *) ip6_gateway, sizeof (*ip6_gateway));
+		if (gw_addr) {
+			rtnl_route_set_gateway (route, gw_addr);
+			rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
+		} else {
+			nm_log_warn (LOGD_DEVICE | LOGD_IP6, "Invalid gateway");
+			rtnl_route_put (route);
+			return NULL;
+		}
+	}
+
+	/* Metric */
+	if (metric)
+		rtnl_route_set_prio (route, metric);
+
+	/* Add the route */
+	err = rtnl_route_add (nlh, route, 0);
+	if (err == -ESRCH && ip6_gateway) {
+		/* Gateway might be over a bridge; try adding a route to gateway first */
+		struct rtnl_route *route2;
+
+		route2 = create_route (iface_idx, mss);
+		if (route2) {
+			/* Add route to gateway over bridge */
+			rtnl_route_set_dst (route2, gw_addr);
+			err = rtnl_route_add (nlh, route2, 0);
+			if (!err) {
+				/* Try adding the route again */
+				err = rtnl_route_add (nlh, route, 0);
+				if (err)
+					rtnl_route_del (nlh, route2, 0);
+			}
+			rtnl_route_put (route2);
+		}
+	}
+
+	if (gw_addr)
+		nl_addr_put (gw_addr);
+
+	if (err) {
+		nm_log_err (LOGD_DEVICE | LOGD_IP6,
+		            "(%s): failed to set IPv6 route: %s",
+		            iface, nl_geterror ());
+		rtnl_route_put (route);
+		route = NULL;
+	}
+
+	return route;
+}
+
+static gboolean
+add_ip6_addresses (NMIP6Config *config, const char *iface)
+{
+	int num_addrs, i, iface_idx;
+	struct rtnl_addr **addrs;
+
+	iface_idx = nm_netlink_iface_to_index (iface);
+
+	num_addrs = nm_ip6_config_get_num_addresses (config);
+	addrs = g_new0 (struct rtnl_addr *, num_addrs + 1);
+
+	for (i = 0; i < num_addrs; i++) {
+		NMIP6Address *addr;
+
+		addr = nm_ip6_config_get_address (config, i);
+		g_assert (addr);
+
+		addrs[i] = nm_ip6_config_to_rtnl_addr (config, i, NM_RTNL_ADDR_DEFAULT);
+		if (!addrs[i]) {
+			nm_log_warn (LOGD_DEVICE | LOGD_IP6,
+			             "(%s): couldn't create rtnl address!",
+			             iface);
+			continue;
+		}
+		rtnl_addr_set_ifindex (addrs[i], iface_idx);
+	}
+
+	return sync_addresses (iface, iface_idx, AF_INET6, addrs, num_addrs);
+}
+
+/*
+ * nm_system_apply_ip6_config
+ *
+ * Set IPv6 configuration of the device from an NMIP6Config object.
+ *
+ */
+gboolean
+nm_system_apply_ip6_config (const char *iface,
+                            NMIP6Config *config,
+                            int priority,
+                            NMIP6ConfigCompareFlags flags)
+{
+	int i;
+
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (config != NULL, FALSE);
+
+	if (flags & NM_IP6_COMPARE_FLAG_ADDRESSES) {
+		if (!add_ip6_addresses (config, iface))
+			return FALSE;
+		sleep (1); // FIXME?
+	}
+
+	if (flags & NM_IP6_COMPARE_FLAG_ROUTES) {
+		for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) {
+			NMIP6Route *route = nm_ip6_config_get_route (config, i);
+			struct rtnl_route *tmp;
+
+			/* Don't add the route if it doesn't have a gateway and the connection
+			 * is never supposed to be the default connection.
+			 */
+			if (   nm_ip6_config_get_never_default (config)
+			    && IN6_IS_ADDR_UNSPECIFIED(nm_ip6_route_get_dest (route)))
+				continue;
+
+			tmp = nm_system_device_set_ip6_route (iface,
+			                                      nm_ip6_route_get_dest (route),
+			                                      nm_ip6_route_get_prefix (route),
+			                                      nm_ip6_route_get_next_hop (route),
+			                                      nm_ip6_route_get_metric (route),
+			                                      nm_ip6_config_get_mss (config));
+			rtnl_route_put (tmp);
+		}
+	}
+
+// FIXME
+//	if (priority > 0)
+//		nm_system_device_set_priority (iface, config, priority);
+
+	return TRUE;
+}
+
+/*
+ * nm_system_device_set_up_down
+ *
+ * Mark the device as up or down.
+ *
+ */
+gboolean
+nm_system_device_set_up_down (NMDevice *dev,
+                              gboolean up,
+                              gboolean *no_firmware)
+{
+	g_return_val_if_fail (dev != NULL, FALSE);
+
+	return nm_system_device_set_up_down_with_iface (nm_device_get_ip_iface (dev), up, no_firmware);
+}
+
+gboolean
+nm_system_device_set_up_down_with_iface (const char *iface,
+                                         gboolean up,
+                                         gboolean *no_firmware)
+{
+	struct rtnl_link *request = NULL, *old = NULL;
+	struct nl_handle *nlh;
+	gboolean success = FALSE;
+	guint32 idx;
+
+	g_return_val_if_fail (iface != NULL, FALSE);
+	if (no_firmware)
+		g_return_val_if_fail (*no_firmware == FALSE, FALSE);
+
+	if (!(request = rtnl_link_alloc ()))
+		goto out;
+
+	if (up)
+		rtnl_link_set_flags (request, IFF_UP);
+	else
+		rtnl_link_unset_flags (request, IFF_UP);
+
+	idx = nm_netlink_iface_to_index (iface);
+	old = nm_netlink_index_to_rtnl_link (idx);
+	if (old) {
+		nlh = nm_netlink_get_default_handle ();
+		if (nlh) {
+			if (rtnl_link_change (nlh, old, request, 0) == 0)
+				success = TRUE;
+			else if ((nl_get_errno () == ENOENT) && no_firmware && up)
+				*no_firmware = TRUE;
+		}
+	}
+
+	rtnl_link_put (old);
+	rtnl_link_put (request);
+
+out:
+	return success;
+}
+
+gboolean
+nm_system_device_is_up (NMDevice *device)
+{
+	g_return_val_if_fail (device != NULL, FALSE);
+
+	return nm_system_device_is_up_with_iface (nm_device_get_ip_iface (device));
+}
+
+gboolean
+nm_system_device_is_up_with_iface (const char *iface)
+{
+	struct ifreq ifr;
+	int fd;
+	gboolean up = FALSE;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_HW, "couldn't open control socket.");
+		return FALSE;
+	}
+
+	/* Get device's flags */
+	memset (&ifr, 0, sizeof (ifr));
+	strncpy (ifr.ifr_name, iface, IFNAMSIZ);
+	if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0) {
+		if (errno != ENODEV) {
+			nm_log_err (LOGD_HW, "(%s): could not get flags: errno %d",
+			            iface, errno);
+		}
+	} else {
+		up = !!(ifr.ifr_flags & IFF_UP);
+	}
+	close (fd);
+
+	return up;
+}
+
+gboolean
+nm_system_device_set_mtu (const char *iface, guint32 mtu)
+{
+	struct rtnl_link *old;
+	struct rtnl_link *new;
+	gboolean success = FALSE;
+	struct nl_handle *nlh;
+	int iface_idx;
+
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (mtu > 0, FALSE);
+
+	new = rtnl_link_alloc ();
+	if (!new)
+		return FALSE;
+
+	iface_idx = nm_netlink_iface_to_index (iface);
+	old = nm_netlink_index_to_rtnl_link (iface_idx);
+	if (old) {
+		rtnl_link_set_mtu (new, mtu);
+		nlh = nm_netlink_get_default_handle ();
+		if (nlh) {
+			rtnl_link_change (nlh, old, new, 0);
+			success = TRUE;
+		}
+		rtnl_link_put (old);
+	}
+
+	rtnl_link_put (new);
+	return success;
+}
+
+static struct rtnl_route *
+add_ip4_route_to_gateway (const char *iface, guint32 gw, guint32 mss)
+{
+	struct nl_handle *nlh;
+	struct rtnl_route *route = NULL;
+	struct nl_addr *gw_addr = NULL;
+	int iface_idx, err;
+
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, NULL);
+
+	iface_idx = nm_netlink_iface_to_index (iface);
+	if (iface_idx < 0)
+		return NULL;
+
+	/* Gateway might be over a bridge; try adding a route to gateway first */
+	route = rtnl_route_alloc ();
+	if (route == NULL)
+		return NULL;
+
+	rtnl_route_set_family (route, AF_INET);
+	rtnl_route_set_table (route, RT_TABLE_MAIN);
+	rtnl_route_set_oif (route, iface_idx);
+	rtnl_route_set_scope (route, RT_SCOPE_LINK);
+
+	gw_addr = nl_addr_build (AF_INET, &gw, sizeof (gw));
+	if (!gw_addr)
+		goto error;
+	nl_addr_set_prefixlen (gw_addr, 32);
+	rtnl_route_set_dst (route, gw_addr);
+	nl_addr_put (gw_addr);
+
+	if (mss) {
+		if (rtnl_route_set_metric (route, RTAX_ADVMSS, mss) < 0)
+			goto error;
+	}
+
+	/* Add direct route to the gateway */
+	err = rtnl_route_add (nlh, route, 0);
+	if (err) {
+		nm_log_err (LOGD_DEVICE | LOGD_IP4,
+		            "(%s): failed to add IPv4 route to gateway (%d)",
+		            iface, err);
+		goto error;
+	}
+
+	return route;
+
+error:
+	rtnl_route_put (route);
+	return NULL;
+}
+
+static int
+replace_default_ip4_route (const char *iface, guint32 gw, guint32 mss)
+{
+	struct rtnl_route *route = NULL;
+	struct nl_handle *nlh;
+	struct nl_addr *dst_addr = NULL;
+	guint32 dst = 0;
+	struct nl_addr *gw_addr = NULL;
+	int iface_idx, err = -1;
+
+	g_return_val_if_fail (iface != NULL, -ENODEV);
+
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, -ENOMEM);
+
+	iface_idx = nm_netlink_iface_to_index (iface);
+	if (iface_idx < 0)
+		return -ENODEV;
+
+	route = rtnl_route_alloc();
+	g_return_val_if_fail (route != NULL, -ENOMEM);
+
+	rtnl_route_set_family (route, AF_INET);
+	rtnl_route_set_table (route, RT_TABLE_MAIN);
+	rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
+	rtnl_route_set_oif (route, iface_idx);
+
+	/* Build up the destination address */
+	dst_addr = nl_addr_build (AF_INET, &dst, sizeof (dst));
+	if (!dst_addr) {
+		err = -ENOMEM;
+		goto out;
+	}
+	nl_addr_set_prefixlen (dst_addr, 0);
+	rtnl_route_set_dst (route, dst_addr);
+
+	/* Build up the gateway address */
+	gw_addr = nl_addr_build (AF_INET, &gw, sizeof (gw));
+	if (!gw_addr) {
+		err = -ENOMEM;
+		goto out;
+	}
+	nl_addr_set_prefixlen (gw_addr, 0);
+	rtnl_route_set_gateway (route, gw_addr);
+
+	if (mss > 0) {
+		err = rtnl_route_set_metric (route, RTAX_ADVMSS, mss);
+		if (err < 0)
+			goto out;
+	}
+
+	/* Add the new default route */
+	err = rtnl_route_add (nlh, route, NLM_F_REPLACE);
+
+out:
+	if (dst_addr)
+		nl_addr_put (dst_addr);
+	if (gw_addr)
+		nl_addr_put (gw_addr);
+	rtnl_route_put (route);
+	return err;
+}
+
+/*
+ * nm_system_replace_default_ip4_route_vpn
+ *
+ * Replace default IPv4 route with one via the current device
+ *
+ */
+gboolean
+nm_system_replace_default_ip4_route_vpn (const char *iface,
+                                         guint32 ext_gw,
+                                         guint32 int_gw,
+                                         guint32 mss,
+                                         const char *parent_iface,
+                                         guint32 parent_mss)
+{
+	struct rtnl_route *gw_route = NULL;
+	struct nl_handle *nlh;
+	gboolean success = FALSE;
+	int err;
+
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, FALSE);
+
+	err = replace_default_ip4_route (iface, int_gw, mss);
+	if (err == 0) {
+		return TRUE;
+	} else if (err != -ESRCH) {
+		nm_log_err (LOGD_DEVICE | LOGD_IP4,
+		            "(%s): failed to set IPv4 default route: %d",
+		            iface, err);
+		return FALSE;
+	}
+
+	/* Try adding a direct route to the gateway first */
+	gw_route = add_ip4_route_to_gateway (parent_iface, ext_gw, parent_mss);
+	if (!gw_route)
+		return FALSE;
+
+	/* Try adding the original route again */
+	err = replace_default_ip4_route (iface, int_gw, mss);
+	if (err != 0) {
+		rtnl_route_del (nlh, gw_route, 0);
+		nm_log_err (LOGD_DEVICE | LOGD_IP4,
+		            "(%s): failed to set IPv4 default route (pass #2): %d",
+		            iface, err);
+	} else
+		success = TRUE;
+
+	rtnl_route_put (gw_route);
+	return success;
+}
+
+/*
+ * nm_system_replace_default_ip4_route
+ *
+ * Replace default IPv4 route with one via the current device
+ *
+ */
+gboolean
+nm_system_replace_default_ip4_route (const char *iface, guint32 gw, guint32 mss)
+{
+	struct rtnl_route *gw_route = NULL;
+	struct nl_handle *nlh;
+	gboolean success = FALSE;
+	int err;
+
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, FALSE);
+
+	err = replace_default_ip4_route (iface, gw, mss);
+	if (err == 0) {
+		return TRUE;
+	} else if (err != -ESRCH) {
+		nm_log_err (LOGD_DEVICE | LOGD_IP4,
+		            "(%s): failed to set IPv4 default route: %d",
+		            iface, err);
+		return FALSE;
+	}
+
+	/* Try adding a direct route to the gateway first */
+	gw_route = add_ip4_route_to_gateway (iface, gw, mss);
+	if (!gw_route)
+		return FALSE;
+
+	/* Try adding the original route again */
+	err = replace_default_ip4_route (iface, gw, mss);
+	if (err != 0) {
+		rtnl_route_del (nlh, gw_route, 0);
+		nm_log_err (LOGD_DEVICE | LOGD_IP4,
+		            "(%s): failed to set IPv4 default route (pass #2): %d",
+		            iface, err);
+	} else
+		success = TRUE;
+
+	rtnl_route_put (gw_route);
+	return success;
+}
+
+static struct rtnl_route *
+add_ip6_route_to_gateway (const char *iface, const struct in6_addr *gw)
+{
+	struct nl_handle *nlh;
+	struct rtnl_route *route = NULL;
+	struct nl_addr *gw_addr = NULL;
+	int iface_idx, err;
+
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, NULL);
+
+	iface_idx = nm_netlink_iface_to_index (iface);
+	if (iface_idx < 0)
+		return NULL;
+
+	/* Gateway might be over a bridge; try adding a route to gateway first */
+	route = rtnl_route_alloc ();
+	if (route == NULL)
+		return NULL;
+
+	rtnl_route_set_family (route, AF_INET6);
+	rtnl_route_set_table (route, RT_TABLE_MAIN);
+	rtnl_route_set_oif (route, iface_idx);
+	rtnl_route_set_scope (route, RT_SCOPE_LINK);
+
+	gw_addr = nl_addr_build (AF_INET, (void *) gw, sizeof (*gw));
+	if (!gw_addr)
+		goto error;
+	nl_addr_set_prefixlen (gw_addr, 128);
+	rtnl_route_set_dst (route, gw_addr);
+	nl_addr_put (gw_addr);
+
+	/* Add direct route to the gateway */
+	err = rtnl_route_add (nlh, route, 0);
+	if (err) {
+		nm_log_err (LOGD_DEVICE | LOGD_IP6,
+		            "(%s): failed to add IPv4 route to gateway (%d)",
+		            iface, err);
+		goto error;
+	}
+
+	return route;
+
+error:
+	rtnl_route_put (route);
+	return NULL;
+}
+
+static int
+replace_default_ip6_route (const char *iface, const struct in6_addr *gw)
+{
+	struct rtnl_route *route = NULL;
+	struct nl_handle *nlh;
+	struct nl_addr *gw_addr = NULL;
+	int iface_idx, err = -1;
+
+	g_return_val_if_fail (iface != NULL, -ENODEV);
+
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, -ENOMEM);
+
+	iface_idx = nm_netlink_iface_to_index (iface);
+	if (iface_idx < 0)
+		return -ENODEV;
+
+	route = rtnl_route_alloc();
+	g_return_val_if_fail (route != NULL, -ENOMEM);
+
+	rtnl_route_set_family (route, AF_INET6);
+	rtnl_route_set_table (route, RT_TABLE_MAIN);
+	rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
+	rtnl_route_set_oif (route, iface_idx);
+
+	if (gw && !IN6_IS_ADDR_UNSPECIFIED (gw)) {
+		/* Build up the gateway address */
+		gw_addr = nl_addr_build (AF_INET6, (void *) gw, sizeof (*gw));
+		if (!gw_addr) {
+			err = -ENOMEM;
+			goto out;
+		}
+		nl_addr_set_prefixlen (gw_addr, -1);
+		rtnl_route_set_gateway (route, gw_addr);
+	}
+
+	/* Add the new default route */
+	err = rtnl_route_add (nlh, route, NLM_F_REPLACE);
+	if (err == -EEXIST) {
+		/* FIXME: even though we use NLM_F_REPLACE the kernel won't replace
+		 * the route if it's the same.  Should try to remove it first, then
+		 * add the new one again here.
+		 */
+		err = 0;
+	}
+
+out:
+	if (gw_addr)
+		nl_addr_put (gw_addr);
+	rtnl_route_put (route);
+	return err;
+}
+
+/*
+ * nm_system_replace_default_ip6_route
+ *
+ * Replace default IPv6 route with one via the given gateway
+ *
+ */
+gboolean
+nm_system_replace_default_ip6_route (const char *iface, const struct in6_addr *gw)
+{
+	struct rtnl_route *gw_route = NULL;
+	struct nl_handle *nlh;
+	gboolean success = FALSE;
+	int err;
+
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, FALSE);
+
+	err = replace_default_ip6_route (iface, gw);
+	if (err == 0) {
+		return TRUE;
+	} else if (err != -ESRCH) {
+		nm_log_err (LOGD_DEVICE | LOGD_IP6,
+		            "(%s): failed to set IPv6 default route: %d",
+		            iface, err);
+		return FALSE;
+	}
+
+	/* Try adding a direct route to the gateway first */
+	gw_route = add_ip6_route_to_gateway (iface, gw);
+	if (!gw_route)
+		return FALSE;
+
+	/* Try adding the original route again */
+	err = replace_default_ip6_route (iface, gw);
+	if (err != 0) {
+		rtnl_route_del (nlh, gw_route, 0);
+		nm_log_err (LOGD_DEVICE | LOGD_IP6,
+		            "(%s): failed to set IPv6 default route (pass #2): %d",
+		            iface, err);
+	} else
+		success = TRUE;
+
+	rtnl_route_put (gw_route);
+	return success;
+}
+
+static void flush_addresses (const char *iface, gboolean ipv4_only)
+{
+	int iface_idx;
+
+	g_return_if_fail (iface != NULL);
+	iface_idx = nm_netlink_iface_to_index (iface);
+	if (iface_idx >= 0)
+		sync_addresses (iface, iface_idx, ipv4_only ? AF_INET : AF_UNSPEC, NULL, 0);
+}
+
+/*
+ * nm_system_device_flush_addresses
+ *
+ * Flush all network addresses associated with a network device
+ *
+ */
+void nm_system_device_flush_addresses (NMDevice *dev)
+{
+	g_return_if_fail (dev != NULL);
+
+	flush_addresses (nm_device_get_ip_iface (dev),
+					 nm_device_get_ip6_config (dev) == NULL);
+}
+
+
+/*
+ * nm_system_device_flush_addresses_with_iface
+ *
+ * Flush all network addresses associated with a network device
+ *
+ */
+void nm_system_device_flush_addresses_with_iface (const char *iface)
+{
+	flush_addresses (iface, FALSE);
+}
+
+
+static void
+foreach_route (void (*callback)(struct nl_object *, gpointer),
+			gpointer user_data)
+{
+	struct nl_handle *nlh;
+	struct nl_cache *route_cache;
+
+	nlh = nm_netlink_get_default_handle ();
+	route_cache = rtnl_route_alloc_cache (nlh);
+	g_assert (route_cache);
+	nl_cache_foreach (route_cache, callback, user_data);
+	nl_cache_free (route_cache);
+}
+
+
+typedef struct {
+	const char *iface;
+	int iface_idx;
+	int family;
+} RouteCheckData;
+
+static void
+check_one_route (struct nl_object *object, void *user_data)
+{
+	RouteCheckData *data = (RouteCheckData *) user_data;
+	struct rtnl_route *route = (struct rtnl_route *) object;
+	int err;
+
+	/* Delete all routes from this interface */
+	if (rtnl_route_get_oif (route) != data->iface_idx)
+		return;
+	if (data->family && rtnl_route_get_family (route) != data->family)
+		return;
+
+	/* We don't want to flush IPv6 link-local routes that may exist on the
+	 * the interface since the LL address and routes should normally stay
+	 * assigned all the time.
+	 */
+	if (   (data->family == AF_INET6 || data->family == AF_UNSPEC)
+	    && (rtnl_route_get_family (route) == AF_INET6)) {
+		struct nl_addr *nl;
+		struct in6_addr *addr = NULL;
+
+		nl = rtnl_route_get_dst (route);
+		if (nl)
+			addr = nl_addr_get_binary_addr (nl);
+
+		if (addr) {
+			if (IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MC_LINKLOCAL (addr))
+				return;
+		}
+	}
+
+	err = rtnl_route_del (nm_netlink_get_default_handle (), route, 0);
+	if (err < 0) {
+		nm_log_err (LOGD_DEVICE,
+		            "(%s): error %d returned from rtnl_route_del(): %s",
+		            data->iface, err, nl_geterror());
+	}
+}
+
+static void flush_routes (int ifindex, const char *iface, int family)
+{
+	RouteCheckData check_data;
+
+	g_return_if_fail (iface != NULL);
+
+	if (ifindex < 0) {
+		ifindex = nm_netlink_iface_to_index (iface);
+		if (ifindex < 0) {
+			nm_log_dbg (LOGD_DEVICE, "(%s) failed to lookup interface index", iface);
+			return;
+		}
+	}
+
+	memset (&check_data, 0, sizeof (check_data));
+	check_data.iface = iface;
+	check_data.iface_idx = ifindex;
+	check_data.family = family;
+	foreach_route (check_one_route, &check_data);
+}
+
+/*
+ * nm_system_device_flush_routes
+ *
+ * Flush all network addresses associated with a network device
+ *
+ */
+void nm_system_device_flush_routes (NMDevice *dev, int family)
+{
+	g_return_if_fail (dev != NULL);
+
+	flush_routes (nm_device_get_ip_ifindex (dev),
+	              nm_device_get_ip_iface (dev),
+	              family);
+}
+
+/*
+ * nm_system_device_flush_routes_with_iface
+ *
+ * Flush all routes associated with a network device.  'family' is an
+ * address family, either AF_INET, AF_INET6, or AF_UNSPEC.
+ *
+ */
+void nm_system_device_flush_routes_with_iface (const char *iface, int family)
+{
+	flush_routes (-1, iface, family);
+}
+
+typedef struct {
+	struct rtnl_route *route;
+	NMIP4Config *config;
+	int iface;
+} SetPriorityInfo;
+
+static void
+find_route (struct nl_object *object, gpointer user_data)
+{
+	struct rtnl_route *route = (struct rtnl_route *) object;
+	SetPriorityInfo *info = (SetPriorityInfo *) user_data;
+	struct nl_addr *dst;
+	struct in_addr *dst_addr;
+	int num;
+	int i;
+
+	if (info->route ||
+	    rtnl_route_get_oif (route) != info->iface ||
+	    rtnl_route_get_scope (route) != RT_SCOPE_LINK)
+		return;
+
+	dst = rtnl_route_get_dst (route);
+	if (nl_addr_get_family (dst) != AF_INET)
+		return;
+
+	dst_addr = nl_addr_get_binary_addr (dst);
+	num = nm_ip4_config_get_num_addresses (info->config);
+	for (i = 0; i < num; i++) {
+		NMIP4Address *addr = nm_ip4_config_get_address (info->config, i);
+		guint32 prefix = nm_ip4_address_get_prefix (addr);
+		guint32 address = nm_ip4_address_get_address (addr);
+
+		if (prefix == nl_addr_get_prefixlen (dst) &&
+		    (address & nm_utils_ip4_prefix_to_netmask (prefix)) == dst_addr->s_addr) {
+
+			/* Ref the route so it sticks around after the cache is cleared */
+			rtnl_route_get (route);
+			info->route = route;
+			break;
+		}
+	}
+}
+
+static void
+nm_system_device_set_priority (const char *iface,
+						 NMIP4Config *config,
+						 int priority)
+{
+	SetPriorityInfo info;
+
+	info.route = NULL;
+	info.config = config;
+	info.iface = nm_netlink_iface_to_index (iface);
+	g_return_if_fail (info.iface >= 0);
+
+	foreach_route (find_route, &info);
+	if (info.route) {
+		struct nl_handle *nlh;
+
+		nlh = nm_netlink_get_default_handle ();
+		rtnl_route_del (nlh, info.route, 0);
+
+		rtnl_route_set_prio (info.route, priority);
+		rtnl_route_add (nlh, info.route, 0);
+		rtnl_route_put (info.route);
+	}
+}
diff --git a/src/nm-system.h b/src/nm-system.h
new file mode 100644
index 00000000..2eee0141
--- /dev/null
+++ b/src/nm-system.h
@@ -0,0 +1,84 @@
+/* -*- 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) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#ifndef NETWORK_MANAGER_SYSTEM_H
+#define NETWORK_MANAGER_SYSTEM_H
+
+#include <netlink/route/rtnl.h>
+#include <netlink/route/route.h>
+
+#include <glib.h>
+#include "nm-device.h"
+#include "nm-ip4-config.h"
+
+/* Prototypes for system/distribution dependent functions,
+ * implemented in the backend files in backends/ directory
+ */
+
+void			nm_system_device_flush_routes				(NMDevice *dev, int family);
+void			nm_system_device_flush_routes_with_iface	(const char *iface, int family);
+
+gboolean		nm_system_replace_default_ip4_route   (const char *iface,
+                                                       guint32 gw,
+                                                       guint32 mss);
+
+gboolean		nm_system_replace_default_ip6_route   (const char *iface,
+                                                       const struct in6_addr *gw);
+
+gboolean		nm_system_replace_default_ip4_route_vpn (const char *iface,
+                                                         guint32 ext_gw,
+                                                         guint32 int_gw,
+                                                         guint32 mss,
+                                                         const char *parent_iface,
+                                                         guint32 parent_mss);
+
+struct rtnl_route *nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device, NMIP4Config *vpn_config);
+
+
+void			nm_system_device_flush_addresses			(NMDevice *dev);
+void			nm_system_device_flush_addresses_with_iface	(const char *iface);
+
+void			nm_system_enable_loopback				(void);
+void			nm_system_update_dns					(void);
+
+gboolean		nm_system_apply_ip4_config              (const char *iface,
+                                                         NMIP4Config *config,
+                                                         int priority,
+                                                         NMIP4ConfigCompareFlags flags);
+
+gboolean		nm_system_apply_ip6_config              (const char *iface,
+                                                         NMIP6Config *config,
+                                                         int priority,
+                                                         NMIP6ConfigCompareFlags flags);
+
+gboolean		nm_system_device_set_up_down				(NMDevice *dev,
+                                                             gboolean up,
+                                                             gboolean *no_firmware);
+gboolean		nm_system_device_set_up_down_with_iface		(const char *iface,
+                                                             gboolean up,
+                                                             gboolean *no_firmware);
+
+gboolean        nm_system_device_is_up (NMDevice *device);
+gboolean        nm_system_device_is_up_with_iface (const char *iface);
+
+gboolean		nm_system_device_set_mtu (const char *iface, guint32 mtu);
+
+#endif
diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c
new file mode 100644
index 00000000..ff0ef68c
--- /dev/null
+++ b/src/nm-udev-manager.c
@@ -0,0 +1,578 @@
+/* -*- 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) 2009 - 2010 Red Hat, Inc.
+ */
+
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "wireless-helper.h"
+
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
+#include <gudev/gudev.h>
+
+#include "nm-udev-manager.h"
+#include "nm-marshal.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+#include "nm-device-wifi.h"
+#include "nm-device-olpc-mesh.h"
+#include "nm-device-ethernet.h"
+
+typedef struct {
+	GUdevClient *client;
+
+	/* Authoritative rfkill state (RFKILL_* enum) */
+	RfKillState rfkill_states[RFKILL_TYPE_MAX];
+	GSList *killswitches;
+
+	gboolean disposed;
+} NMUdevManagerPrivate;
+
+#define NM_UDEV_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_UDEV_MANAGER, NMUdevManagerPrivate))
+
+G_DEFINE_TYPE (NMUdevManager, nm_udev_manager, G_TYPE_OBJECT)
+
+enum {
+	DEVICE_ADDED,
+	DEVICE_REMOVED,
+	RFKILL_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+typedef struct {
+	char *name;
+	guint64 seqnum;
+	char *path;
+	char *driver;
+	RfKillType rtype;
+	gint state;
+} Killswitch;
+
+RfKillState
+nm_udev_manager_get_rfkill_state (NMUdevManager *self, RfKillType rtype)
+{
+	g_return_val_if_fail (self != NULL, RFKILL_UNBLOCKED);
+	g_return_val_if_fail (rtype < RFKILL_TYPE_MAX, RFKILL_UNBLOCKED);
+
+	return NM_UDEV_MANAGER_GET_PRIVATE (self)->rfkill_states[rtype];
+}
+
+static const char *
+rfkill_type_to_desc (RfKillType rtype)
+{
+	if (rtype == 0)
+		return "WiFi";
+	else if (rtype == 1)
+		return "WWan";
+	else if (rtype == 2)
+		return "WiMAX";
+	return "unknown";
+}
+
+static const char *
+rfkill_state_to_desc (RfKillState rstate)
+{
+	if (rstate == 0)
+		return "unblocked";
+	else if (rstate == 1)
+		return "soft-blocked";
+	else if (rstate == 2)
+		return "hard-blocked";
+	return "unknown";
+}
+
+static Killswitch *
+killswitch_new (GUdevDevice *device, RfKillType rtype)
+{
+	Killswitch *ks;
+	GUdevDevice *parent = NULL;
+	const char *driver;
+
+	ks = g_malloc0 (sizeof (Killswitch));
+	ks->name = g_strdup (g_udev_device_get_name (device));
+	ks->seqnum = g_udev_device_get_seqnum (device);
+	ks->path = g_strdup (g_udev_device_get_sysfs_path (device));
+	ks->rtype = rtype;
+
+	driver = g_udev_device_get_property (device, "DRIVER");
+	if (!driver) {
+		parent = g_udev_device_get_parent (device);
+		if (parent)
+			driver = g_udev_device_get_property (parent, "DRIVER");
+	}
+	if (driver)
+		ks->driver = g_strdup (driver);
+
+	if (parent)
+		g_object_unref (parent);
+
+	return ks;
+}
+
+static void
+killswitch_destroy (Killswitch *ks)
+{
+	g_return_if_fail (ks != NULL);
+
+	g_free (ks->name);
+	g_free (ks->path);
+	g_free (ks->driver);
+	memset (ks, 0, sizeof (Killswitch));
+	g_free (ks);
+}
+
+NMUdevManager *
+nm_udev_manager_new (void)
+{
+	return NM_UDEV_MANAGER (g_object_new (NM_TYPE_UDEV_MANAGER, NULL));
+}
+
+static RfKillState
+sysfs_state_to_nm_state (gint sysfs_state)
+{
+	switch (sysfs_state) {
+	case 0:
+		return RFKILL_SOFT_BLOCKED;
+	case 1:
+		return RFKILL_UNBLOCKED;
+	case 2:
+		return RFKILL_HARD_BLOCKED;
+	default:
+		nm_log_warn (LOGD_RFKILL, "unhandled rfkill state %d", sysfs_state);
+		break;
+	}
+	return RFKILL_UNBLOCKED;
+}
+
+static void
+recheck_killswitches (NMUdevManager *self)
+{
+	NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+	RfKillState poll_states[RFKILL_TYPE_MAX];
+	int i;
+
+	/* Default state is unblocked */
+	for (i = 0; i < RFKILL_TYPE_MAX; i++)
+		poll_states[i] = RFKILL_UNBLOCKED;
+
+	for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) {
+		Killswitch *ks = iter->data;
+		GUdevDevice *device;
+		RfKillState dev_state;
+
+		device = g_udev_client_query_by_subsystem_and_name (priv->client, "rfkill", ks->name);
+		if (!device)
+			continue;
+
+		dev_state = sysfs_state_to_nm_state (g_udev_device_get_property_as_int (device, "RFKILL_STATE"));
+		if (dev_state > poll_states[ks->rtype])
+			poll_states[ks->rtype] = dev_state;
+
+		g_object_unref (device);
+	}
+
+	for (i = 0; i < RFKILL_TYPE_MAX; i++) {
+		if (poll_states[i] != priv->rfkill_states[i]) {
+			nm_log_dbg (LOGD_RFKILL, "%s rfkill state now '%s'",
+			            rfkill_type_to_desc (i),
+			            rfkill_state_to_desc (poll_states[i]));
+
+			priv->rfkill_states[i] = poll_states[i];
+			g_signal_emit (self, signals[RFKILL_CHANGED], 0, i, priv->rfkill_states[i]);
+		}
+	}
+}
+
+static Killswitch *
+killswitch_find_by_name (NMUdevManager *self, const char *name)
+{
+	NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) {
+		Killswitch *candidate = iter->data;
+
+		if (!strcmp (name, candidate->name))
+			return candidate;
+	}
+	return NULL;
+}
+
+static const RfKillType
+rfkill_type_to_enum (const char *str)
+{
+	g_return_val_if_fail (str != NULL, RFKILL_TYPE_UNKNOWN);
+
+	if (!strcmp (str, "wlan"))
+		return RFKILL_TYPE_WLAN;
+	else if (!strcmp (str, "wwan"))
+		return RFKILL_TYPE_WWAN;
+	else if (!strcmp (str, "wimax"))
+		return RFKILL_TYPE_WIMAX;
+
+	return RFKILL_TYPE_UNKNOWN;
+}
+
+static void
+add_one_killswitch (NMUdevManager *self, GUdevDevice *device)
+{
+	NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self);
+	const char *str_type;
+	RfKillType rtype;
+	Killswitch *ks;
+
+	str_type = g_udev_device_get_property (device, "RFKILL_TYPE");
+	rtype = rfkill_type_to_enum (str_type);
+	if (rtype == RFKILL_TYPE_UNKNOWN)
+		return;
+
+	ks = killswitch_new (device, rtype);
+	priv->killswitches = g_slist_prepend (priv->killswitches, ks);
+
+	nm_log_info (LOGD_RFKILL, "found %s radio killswitch %s (at %s) (driver %s)",
+	             rfkill_type_to_desc (rtype),
+	             ks->name,
+	             ks->path,
+	             ks->driver ? ks->driver : "<unknown>");
+}
+
+static void
+rfkill_add (NMUdevManager *self, GUdevDevice *device)
+{
+	const char *name;
+
+	g_return_if_fail (device != NULL);
+	name = g_udev_device_get_name (device);
+	g_return_if_fail (name != NULL);
+
+	if (!killswitch_find_by_name (self, name))
+		add_one_killswitch (self, device);
+}
+
+static void
+rfkill_remove (NMUdevManager *self,
+               GUdevDevice *device)
+{
+	NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+	const char *name;
+
+	g_return_if_fail (device != NULL);
+	name = g_udev_device_get_name (device);
+	g_return_if_fail (name != NULL);
+
+	for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) {
+		Killswitch *ks = iter->data;
+
+		if (!strcmp (ks->name, name)) {
+			nm_log_info (LOGD_RFKILL, "radio killswitch %s disappeared", ks->path);
+			priv->killswitches = g_slist_remove (priv->killswitches, ks);
+			killswitch_destroy (ks);
+			break;
+		}
+	}
+}
+
+static gboolean
+is_wireless (GUdevDevice *device)
+{
+	char phy80211_path[255];
+	struct stat s;
+	int fd;
+	struct iwreq iwr;
+	const char *ifname, *path;
+	gboolean is_wifi = FALSE;
+
+	ifname = g_udev_device_get_name (device);
+	g_assert (ifname);
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	strncpy (iwr.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ);
+
+	path = g_udev_device_get_sysfs_path (device);
+	snprintf (phy80211_path, sizeof (phy80211_path), "%s/phy80211", path);
+
+	if (   (ioctl (fd, SIOCGIWNAME, &iwr) == 0)
+	    || (stat (phy80211_path, &s) == 0 && (s.st_mode & S_IFDIR)))
+		is_wifi = TRUE;
+
+	close (fd);
+	return is_wifi;
+}
+
+static gboolean
+is_olpc_mesh (GUdevDevice *device)
+{
+	const gchar *prop = g_udev_device_get_property (device, "ID_NM_OLPC_MESH");
+	return (prop != NULL);
+}
+
+static GObject *
+device_creator (NMUdevManager *manager,
+                GUdevDevice *udev_device,
+                gboolean sleeping)
+{
+	GObject *device = NULL;
+	const char *ifname, *driver, *path, *subsys;
+	GUdevDevice *parent = NULL, *grandparent = NULL;
+	gint ifindex;
+
+	ifname = g_udev_device_get_name (udev_device);
+	g_assert (ifname);
+
+	path = g_udev_device_get_sysfs_path (udev_device);
+	if (!path) {
+		nm_log_warn (LOGD_HW, "couldn't determine device path; ignoring...");
+		return NULL;
+	}
+
+	driver = g_udev_device_get_driver (udev_device);
+	if (!driver) {
+		/* Try the parent */
+		parent = g_udev_device_get_parent (udev_device);
+		if (parent) {
+			driver = g_udev_device_get_driver (parent);
+			if (!driver) {
+				/* try the grandparent only if it's an ibmebus device */
+				subsys = g_udev_device_get_subsystem (parent);
+				if (subsys && !strcmp (subsys, "ibmebus")) {
+					grandparent = g_udev_device_get_parent (parent);
+					if (grandparent)
+						driver = g_udev_device_get_driver (grandparent);
+				}
+			}
+		}
+	}
+
+	if (!driver) {
+		nm_log_warn (LOGD_HW, "%s: couldn't determine device driver; ignoring...", path);
+		goto out;
+	}
+
+	ifindex = g_udev_device_get_sysfs_attr_as_int (udev_device, "ifindex");
+	if (ifindex <= 0) {
+		nm_log_warn (LOGD_HW, "%s: device had invalid ifindex %d; ignoring...", path, (guint32) ifindex);
+		goto out;
+	}
+
+	if (is_olpc_mesh (udev_device)) /* must be before is_wireless */
+		device = (GObject *) nm_device_olpc_mesh_new (path, ifname, driver);
+	else if (is_wireless (udev_device))
+		device = (GObject *) nm_device_wifi_new (path, ifname, driver);
+	else
+		device = (GObject *) nm_device_ethernet_new (path, ifname, driver);
+
+out:
+	if (grandparent)
+		g_object_unref (grandparent);
+	if (parent)
+		g_object_unref (parent);
+	return device;
+}
+
+static void
+net_add (NMUdevManager *self, GUdevDevice *device)
+{
+	gint etype;
+	const char *iface;
+	const char *devtype;
+
+	g_return_if_fail (device != NULL);
+
+	etype = g_udev_device_get_sysfs_attr_as_int (device, "type");
+	if (etype != 1) {
+		nm_log_dbg (LOGD_HW, "ignoring interface with type %d", etype);
+		return; /* Not using ethernet encapsulation, don't care */
+	}
+
+	/* Not all ethernet devices are immediately usable; newer mobile broadband
+	 * devices (Ericsson, Option, Sierra) require setup on the tty before the
+	 * ethernet device is usable.  2.6.33 and later kernels set the 'DEVTYPE'
+	 * uevent variable which we can use to ignore the interface as a NMDevice
+	 * subclass.  ModemManager will pick it up though and so we'll handle it
+	 * through the mobile broadband stuff.
+	 */
+	devtype = g_udev_device_get_property (device, "DEVTYPE");
+	if (devtype && !strcmp (devtype, "wwan")) {
+		nm_log_dbg (LOGD_HW, "ignoring interface with devtype '%s'", devtype);
+		return;
+	}
+
+	iface = g_udev_device_get_name (device);
+	if (!iface) {
+		nm_log_dbg (LOGD_HW, "failed to get device's interface");
+		return;
+	}
+
+	g_signal_emit (self, signals[DEVICE_ADDED], 0, device, device_creator);
+}
+
+static void
+net_remove (NMUdevManager *self, GUdevDevice *device)
+{
+	g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
+}
+
+void
+nm_udev_manager_query_devices (NMUdevManager *self)
+{
+	NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self);
+	GList *devices, *iter;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_UDEV_MANAGER (self));
+
+	devices = g_udev_client_query_by_subsystem (priv->client, "net");
+	for (iter = devices; iter; iter = g_list_next (iter)) {
+		net_add (self, G_UDEV_DEVICE (iter->data));
+		g_object_unref (G_UDEV_DEVICE (iter->data));
+	}
+	g_list_free (devices);
+}
+
+static void
+handle_uevent (GUdevClient *client,
+               const char *action,
+               GUdevDevice *device,
+               gpointer user_data)
+{
+	NMUdevManager *self = NM_UDEV_MANAGER (user_data);
+	const char *subsys;
+
+	g_return_if_fail (action != NULL);
+
+	/* A bit paranoid */
+	subsys = g_udev_device_get_subsystem (device);
+	g_return_if_fail (subsys != NULL);
+
+	nm_log_dbg (LOGD_HW, "UDEV event: action '%s' subsys '%s' device '%s'",
+	            action, subsys, g_udev_device_get_name (device));
+
+	g_return_if_fail (!strcmp (subsys, "rfkill") || !strcmp (subsys, "net"));
+
+	if (!strcmp (action, "add")) {
+		if (!strcmp (subsys, "rfkill"))
+			rfkill_add (self, device);
+		else if (!strcmp (subsys, "net"))
+			net_add (self, device);
+	} else if (!strcmp (action, "remove")) {
+		if (!strcmp (subsys, "rfkill"))
+			rfkill_remove (self, device);
+		else if (!strcmp (subsys, "net"))
+			net_remove (self, device);
+	}
+
+	recheck_killswitches (self);
+}
+
+static void
+nm_udev_manager_init (NMUdevManager *self)
+{
+	NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self);
+	const char *subsys[3] = { "rfkill", "net", NULL };
+	GList *switches, *iter;
+	guint32 i;
+
+	for (i = 0; i < RFKILL_TYPE_MAX; i++)
+		priv->rfkill_states[i] = RFKILL_UNBLOCKED;
+
+	priv->client = g_udev_client_new (subsys);
+	g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self);
+
+	switches = g_udev_client_query_by_subsystem (priv->client, "rfkill");
+	for (iter = switches; iter; iter = g_list_next (iter)) {
+		add_one_killswitch (self, G_UDEV_DEVICE (iter->data));
+		g_object_unref (G_UDEV_DEVICE (iter->data));
+	}
+	g_list_free (switches);
+
+	recheck_killswitches (self);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMUdevManager *self = NM_UDEV_MANAGER (object);
+	NMUdevManagerPrivate *priv = NM_UDEV_MANAGER_GET_PRIVATE (self);
+
+	if (priv->disposed) {
+		G_OBJECT_CLASS (nm_udev_manager_parent_class)->dispose (object);
+		return;
+	}
+	priv->disposed = TRUE;
+
+	g_object_unref (priv->client);
+
+	g_slist_foreach (priv->killswitches, (GFunc) killswitch_destroy, NULL);
+	g_slist_free (priv->killswitches);
+
+	G_OBJECT_CLASS (nm_udev_manager_parent_class)->dispose (object);	
+}
+
+static void
+nm_udev_manager_class_init (NMUdevManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMUdevManagerPrivate));
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+
+	/* Signals */
+	signals[DEVICE_ADDED] =
+		g_signal_new ("device-added",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMUdevManagerClass, device_added),
+					  NULL, NULL,
+					  _nm_marshal_VOID__POINTER_POINTER,
+					  G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
+
+	signals[DEVICE_REMOVED] =
+		g_signal_new ("device-removed",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMUdevManagerClass, device_removed),
+					  NULL, NULL,
+					  g_cclosure_marshal_VOID__POINTER,
+					  G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	signals[RFKILL_CHANGED] =
+		g_signal_new ("rfkill-changed",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMUdevManagerClass, rfkill_changed),
+					  NULL, NULL,
+					  _nm_marshal_VOID__UINT_UINT,
+					  G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+}
+
diff --git a/src/nm-udev-manager.h b/src/nm-udev-manager.h
new file mode 100644
index 00000000..06a2d35c
--- /dev/null
+++ b/src/nm-udev-manager.h
@@ -0,0 +1,72 @@
+/* -*- 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) 2007 - 2008 Novell, Inc.
+ * Copyright (C) 2007 - 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_UDEV_MANAGER_H
+#define NM_UDEV_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
+#include <gudev/gudev.h>
+
+#include "nm-rfkill.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_UDEV_MANAGER            (nm_udev_manager_get_type ())
+#define NM_UDEV_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_UDEV_MANAGER, NMUdevManager))
+#define NM_UDEV_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_UDEV_MANAGER, NMUdevManagerClass))
+#define NM_IS_UDEV_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_UDEV_MANAGER))
+#define NM_IS_UDEV_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_UDEV_MANAGER))
+#define NM_UDEV_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_UDEV_MANAGER, NMUdevManagerClass))
+
+typedef struct {
+	GObject parent;
+} NMUdevManager;
+
+typedef GObject *(*NMDeviceCreatorFn) (NMUdevManager *manager,
+                                       GUdevDevice *device,
+                                       gboolean sleeping);
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Virtual functions */
+	void (*device_added) (NMUdevManager *manager,
+	                      GUdevDevice *device,
+	                      NMDeviceCreatorFn creator_fn);
+
+	void (*device_removed) (NMUdevManager *manager, GUdevDevice *device);
+
+	void (*rfkill_changed) (NMUdevManager *manager, RfKillType rtype, RfKillState state);
+} NMUdevManagerClass;
+
+GType nm_udev_manager_get_type (void);
+
+NMUdevManager *nm_udev_manager_new (void);
+
+void nm_udev_manager_query_devices (NMUdevManager *manager);
+
+RfKillState nm_udev_manager_get_rfkill_state (NMUdevManager *manager, RfKillType rtype);
+
+#endif /* NM_UDEV_MANAGER_H */
+
diff --git a/src/nm-wifi-ap.c b/src/nm-wifi-ap.c
new file mode 100644
index 00000000..c7b5d8a5
--- /dev/null
+++ b/src/nm-wifi-ap.c
@@ -0,0 +1,1477 @@
+/* -*- 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) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include "wireless-helper.h"
+
+#include <string.h>
+
+#include "nm-wifi-ap.h"
+#include "NetworkManagerUtils.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "nm-dbus-manager.h"
+#include "wpa.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-setting-wireless.h"
+
+#include "nm-access-point-glue.h"
+
+/*
+ * Encapsulates Access Point information
+ */
+typedef struct
+{
+	char *dbus_path;
+
+	/* Scanned or cached values */
+	GByteArray *	ssid;
+	struct ether_addr	address;
+	NM80211Mode		mode;
+	gint8			strength;
+	guint32			freq;		/* Frequency in MHz; ie 2412 (== 2.412 GHz) */
+	guint32			max_bitrate;/* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
+
+	guint32			flags;		/* General flags */
+	guint32			wpa_flags;	/* WPA-related flags */
+	guint32			rsn_flags;	/* RSN (WPA2) -related flags */
+
+	/* Non-scanned attributes */
+	gboolean			fake;	/* Whether or not the AP is from a scan */
+	gboolean			broadcast;	/* Whether or not the AP is broadcasting (hidden) */
+	gboolean			user_created;	/* Whether or not the AP was created
+										 * by the user with "Create network..."
+										 * A subset of Ad-Hoc mode.  user_created
+										 * implies Ad-Hoc, but not necessarily
+										 * the other way around.
+										 */
+	glong				last_seen;	/* Last time the AP was seen in a scan in seconds */
+
+	/* Things from user prefs/NetworkManagerInfo */
+	GTimeVal			timestamp;
+	GSList *			user_addresses;
+} NMAccessPointPrivate;
+
+#define NM_AP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AP, NMAccessPointPrivate))
+
+G_DEFINE_TYPE (NMAccessPoint, nm_ap, G_TYPE_OBJECT)
+
+enum {
+	PROPERTIES_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_0,
+	PROP_FLAGS,
+	PROP_WPA_FLAGS,
+	PROP_RSN_FLAGS,
+	PROP_SSID,
+	PROP_FREQUENCY,
+	PROP_HW_ADDRESS,
+	PROP_MODE,
+	PROP_MAX_BITRATE,
+	PROP_STRENGTH,
+	LAST_PROP
+};
+
+static void
+nm_ap_init (NMAccessPoint *ap)
+{
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (ap);
+
+	priv->dbus_path = NULL;
+	priv->mode = NM_802_11_MODE_INFRA;
+	priv->flags = NM_802_11_AP_FLAGS_NONE;
+	priv->wpa_flags = NM_802_11_AP_SEC_NONE;
+	priv->rsn_flags = NM_802_11_AP_SEC_NONE;
+	priv->broadcast = TRUE;
+}
+
+static void
+finalize (GObject *object)
+{
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
+
+	g_free (priv->dbus_path);
+	if (priv->ssid)
+		g_byte_array_free (priv->ssid, TRUE);
+	g_slist_foreach (priv->user_addresses, (GFunc)g_free, NULL);
+	g_slist_free (priv->user_addresses);
+
+	G_OBJECT_CLASS (nm_ap_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMAccessPoint *ap = NM_AP (object);
+
+	switch (prop_id) {
+	case PROP_FLAGS:
+		nm_ap_set_flags (ap, g_value_get_uint (value));
+		break;
+	case PROP_WPA_FLAGS:
+		nm_ap_set_wpa_flags (ap, g_value_get_uint (value));
+		break;
+	case PROP_RSN_FLAGS:
+		nm_ap_set_rsn_flags (ap, g_value_get_uint (value));
+		break;
+	case PROP_SSID:
+		nm_ap_set_ssid (ap, (GByteArray *) g_value_get_boxed (value));
+		break;
+	case PROP_FREQUENCY:
+		nm_ap_set_freq (ap, g_value_get_uint (value));
+		break;
+	case PROP_MODE:
+		nm_ap_set_mode (ap, g_value_get_uint (value));
+		break;
+	case PROP_MAX_BITRATE:
+		nm_ap_set_max_bitrate (ap, g_value_get_uint (value));
+		break;
+	case PROP_STRENGTH:
+		nm_ap_set_strength (ap, g_value_get_char (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
+	GArray * ssid;
+	int len;
+	int i;
+
+	switch (prop_id) {
+	case PROP_FLAGS:
+		g_value_set_uint (value, priv->flags);
+		break;
+	case PROP_WPA_FLAGS:
+		g_value_set_uint (value, priv->wpa_flags);
+		break;
+	case PROP_RSN_FLAGS:
+		g_value_set_uint (value, priv->rsn_flags);
+		break;
+	case PROP_SSID:
+		len = priv->ssid ? priv->ssid->len : 0;
+		ssid = g_array_sized_new (FALSE, TRUE, sizeof (unsigned char), len);
+		for (i = 0; i < len; i++)
+			g_array_append_val (ssid, priv->ssid->data[i]);
+		g_value_set_boxed (value, ssid);
+		g_array_free (ssid, TRUE);
+		break;
+	case PROP_FREQUENCY:
+		g_value_set_uint (value, priv->freq);
+		break;
+	case PROP_HW_ADDRESS:
+		g_value_take_string (value, nm_ether_ntop (&priv->address));
+		break;
+	case PROP_MODE:
+		g_value_set_uint (value, priv->mode);
+		break;
+	case PROP_MAX_BITRATE:
+		g_value_set_uint (value, priv->max_bitrate);
+		break;
+	case PROP_STRENGTH:
+		g_value_set_char (value, priv->strength);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_ap_class_init (NMAccessPointClass *ap_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (ap_class);
+	guint32 all_sec_flags;
+
+	g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	/* properties */
+
+	all_sec_flags =   NM_802_11_AP_SEC_NONE
+	                | NM_802_11_AP_SEC_PAIR_WEP40
+	                | NM_802_11_AP_SEC_PAIR_WEP104
+	                | NM_802_11_AP_SEC_PAIR_TKIP
+	                | NM_802_11_AP_SEC_PAIR_CCMP
+	                | NM_802_11_AP_SEC_GROUP_WEP40
+	                | NM_802_11_AP_SEC_GROUP_WEP104
+	                | NM_802_11_AP_SEC_GROUP_TKIP
+	                | NM_802_11_AP_SEC_GROUP_CCMP
+	                | NM_802_11_AP_SEC_KEY_MGMT_PSK
+	                | NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+
+	g_object_class_install_property
+		(object_class, PROP_FLAGS,
+		 g_param_spec_uint (NM_AP_FLAGS,
+							"Flags",
+							"Flags",
+							NM_802_11_AP_FLAGS_NONE,
+							NM_802_11_AP_FLAGS_PRIVACY,
+							NM_802_11_AP_FLAGS_NONE,
+							G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_WPA_FLAGS,
+		 g_param_spec_uint (NM_AP_WPA_FLAGS,
+							"WPA Flags",
+							"WPA Flags",
+							NM_802_11_AP_SEC_NONE,
+							all_sec_flags,
+							NM_802_11_AP_SEC_NONE,
+							G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_RSN_FLAGS,
+		 g_param_spec_uint (NM_AP_RSN_FLAGS,
+							"RSN Flags",
+							"RSN Flags",
+							NM_802_11_AP_SEC_NONE,
+							all_sec_flags,
+							NM_802_11_AP_SEC_NONE,
+							G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_SSID,
+	     g_param_spec_boxed (NM_AP_SSID,
+	                         "SSID",
+	                         "SSID",
+	                         DBUS_TYPE_G_UCHAR_ARRAY,
+	                         G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_FREQUENCY,
+		 g_param_spec_uint (NM_AP_FREQUENCY,
+							"Frequency",
+							"Frequency",
+							0, 10000, 0,
+							G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_HW_ADDRESS,
+		 g_param_spec_string (NM_AP_HW_ADDRESS,
+							  "MAC Address",
+							  "Hardware MAC address",
+							  NULL,
+							  G_PARAM_READABLE));
+	
+	g_object_class_install_property
+		(object_class, PROP_MODE,
+		 g_param_spec_uint (NM_AP_MODE,
+						   "Mode",
+						   "Mode",
+						   NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA,
+						   G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_MAX_BITRATE,
+		 g_param_spec_uint (NM_AP_MAX_BITRATE,
+							"Max Bitrate",
+							"Max Bitrate",
+							0, G_MAXUINT16, 0,
+							G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_STRENGTH,
+		 g_param_spec_char (NM_AP_STRENGTH,
+							"Strength",
+							"Strength",
+							G_MININT8, G_MAXINT8, 0,
+							G_PARAM_READWRITE));
+
+	/* Signals */
+	signals[PROPERTIES_CHANGED] = 
+		nm_properties_changed_signal_new (object_class,
+								    G_STRUCT_OFFSET (NMAccessPointClass, properties_changed));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (ap_class),
+							   &dbus_glib_nm_access_point_object_info);
+}
+
+void
+nm_ap_export_to_dbus (NMAccessPoint *ap)
+{
+	NMAccessPointPrivate *priv;
+	NMDBusManager *mgr;
+	DBusGConnection *g_connection;
+	static guint32 counter = 0;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->dbus_path) {
+		nm_log_err (LOGD_CORE, "Tried to export AP %s twice.", priv->dbus_path);
+		return;
+	}
+
+	mgr = nm_dbus_manager_get ();
+	g_assert (mgr);
+
+	g_connection = nm_dbus_manager_get_connection (mgr);
+	g_assert (g_connection);
+
+	priv->dbus_path = g_strdup_printf (NM_DBUS_PATH_ACCESS_POINT "/%d", counter++);
+	dbus_g_connection_register_g_object (g_connection, priv->dbus_path, G_OBJECT (ap));
+
+	g_object_unref (mgr);
+}
+
+/*
+ * nm_ap_new
+ *
+ * Create a new, blank user access point info structure
+ *
+ */
+NMAccessPoint *nm_ap_new (void)
+{
+	GObject *object;
+
+	object = g_object_new (NM_TYPE_AP, NULL);
+	if (!object)
+		return NULL;
+
+	return (NMAccessPoint *) object;
+}
+
+
+#define IEEE80211_CAP_ESS       0x0001
+#define IEEE80211_CAP_IBSS      0x0002
+#define IEEE80211_CAP_PRIVACY   0x0010
+
+static void
+foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	GValue *variant = (GValue *) value;
+	NMAccessPoint *ap = (NMAccessPoint *) user_data;
+
+	if (G_VALUE_HOLDS_BOXED (variant)) {
+		GArray *array = g_value_get_boxed (variant);
+
+		if (!strcmp (key, "ssid")) {
+			guint32 len = MIN (IW_ESSID_MAX_SIZE, array->len);
+			GByteArray * ssid;
+
+			/* Stupid ieee80211 layer uses <hidden> */
+			if (((len == 8) || (len == 9))
+				&& (memcmp (array->data, "<hidden>", 8) == 0))
+				return;
+
+			if (nm_utils_is_empty_ssid ((const guint8 *) array->data, len))
+				return;
+
+			ssid = g_byte_array_sized_new (len);
+			g_byte_array_append (ssid, (const guint8 *) array->data, len);
+			nm_ap_set_ssid (ap, ssid);
+			g_byte_array_free (ssid, TRUE);
+		} else if (!strcmp (key, "bssid")) {
+			struct ether_addr addr;
+
+			if (array->len != ETH_ALEN)
+				return;
+			memset (&addr, 0, sizeof (struct ether_addr));
+			memcpy (&addr, array->data, ETH_ALEN);
+			nm_ap_set_address (ap, &addr);
+		} else if (!strcmp (key, "wpaie")) {
+			guint8 * ie = (guint8 *) array->data;
+			guint32 flags = nm_ap_get_wpa_flags (ap);
+
+			if (array->len <= 0 || array->len > WPA_MAX_IE_LEN)
+				return;
+			flags = nm_ap_add_security_from_ie (flags, ie, array->len);
+			nm_ap_set_wpa_flags (ap, flags);
+		} else if (!strcmp (key, "rsnie")) {
+			guint8 * ie = (guint8 *) array->data;
+			guint32 flags = nm_ap_get_rsn_flags (ap);
+
+			if (array->len <= 0 || array->len > WPA_MAX_IE_LEN)
+				return;
+			flags = nm_ap_add_security_from_ie (flags, ie, array->len);
+			nm_ap_set_rsn_flags (ap, flags);
+		}
+	} else if (G_VALUE_HOLDS_INT (variant)) {
+		gint32 int_val = g_value_get_int (variant);
+
+		if (!strcmp (key, "frequency")) {
+			nm_ap_set_freq (ap, (guint32) int_val);
+		} else if (!strcmp (key, "maxrate")) {
+			/* Supplicant reports as b/s, we use Kb/s internally */
+			nm_ap_set_max_bitrate (ap, int_val / 1000);
+		}
+	} else if (G_VALUE_HOLDS_UINT (variant)) {
+		guint32 val = g_value_get_uint (variant);
+
+		if (!strcmp (key, "capabilities")) {
+			if (val & IEEE80211_CAP_ESS) {
+				nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+			} else if (val & IEEE80211_CAP_IBSS) {
+				nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
+			}
+
+			if (val & IEEE80211_CAP_PRIVACY) {
+				guint32 flags = nm_ap_get_flags (ap);
+				nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY);
+			}
+		}
+	}
+}
+
+
+NMAccessPoint *
+nm_ap_new_from_properties (GHashTable *properties)
+{
+	NMAccessPoint *ap;
+	GTimeVal cur_time;
+	const struct ether_addr * addr;
+	const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+	g_return_val_if_fail (properties != NULL, NULL);
+
+	ap = nm_ap_new ();
+
+	g_object_freeze_notify (G_OBJECT (ap));
+	g_hash_table_foreach (properties, foreach_property_cb, ap);
+
+	/* ignore APs with invalid BSSIDs */
+	addr = nm_ap_get_address (ap);
+	if (   !(memcmp (addr->ether_addr_octet, bad_bssid1, ETH_ALEN))
+	    || !(memcmp (addr->ether_addr_octet, bad_bssid2, ETH_ALEN))) {
+		g_object_unref (ap);
+		return NULL;
+	}
+
+	g_get_current_time (&cur_time);
+	nm_ap_set_last_seen (ap, cur_time.tv_sec);
+
+	if (!nm_ap_get_ssid (ap))
+		nm_ap_set_broadcast (ap, FALSE);
+
+	g_object_thaw_notify (G_OBJECT (ap));
+
+	return ap;
+}
+
+#define PROTO_WPA "wpa"
+#define PROTO_RSN "rsn"
+
+static gboolean
+has_proto (NMSettingWirelessSecurity *sec, const char *proto)
+{
+	guint32 num_protos = nm_setting_wireless_security_get_num_protos (sec);
+	guint32 i;
+
+	if (num_protos == 0)
+		return TRUE; /* interpret no protos as "all" */
+
+	for (i = 0; i < num_protos; i++) {
+		if (!strcmp (nm_setting_wireless_security_get_proto (sec, i), proto))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static void
+add_pair_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec)
+{
+	guint32 num = nm_setting_wireless_security_get_num_pairwise (sec);
+	guint32 flags = NM_802_11_AP_SEC_NONE;
+	guint32 i;
+
+	/* If no ciphers are specified, that means "all" WPA ciphers */
+	if (num == 0) {
+		flags |= NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP;
+	} else {
+		for (i = 0; i < num; i++) {
+			const char *cipher = nm_setting_wireless_security_get_pairwise (sec, i);
+
+			if (!strcmp (cipher, "tkip"))
+				flags |= NM_802_11_AP_SEC_PAIR_TKIP;
+			else if (!strcmp (cipher, "ccmp"))
+				flags |= NM_802_11_AP_SEC_PAIR_CCMP;
+		}
+	}
+
+	if (has_proto (sec, PROTO_WPA))
+		nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags);
+	if (has_proto (sec, PROTO_RSN))
+		nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags);
+}
+
+static void
+add_group_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec)
+{
+	guint32 num = nm_setting_wireless_security_get_num_groups (sec);
+	guint32 flags = NM_802_11_AP_SEC_NONE;
+	guint32 i;
+
+	/* If no ciphers are specified, that means "all" WPA ciphers */
+	if (num == 0) {
+		flags |= NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_GROUP_CCMP;
+	} else {
+		for (i = 0; i < num; i++) {
+			const char *cipher = nm_setting_wireless_security_get_group (sec, i);
+
+			if (!strcmp (cipher, "wep40"))
+				flags |= NM_802_11_AP_SEC_GROUP_WEP40;
+			else if (!strcmp (cipher, "wep104"))
+				flags |= NM_802_11_AP_SEC_GROUP_WEP104;
+			else if (!strcmp (cipher, "tkip"))
+				flags |= NM_802_11_AP_SEC_GROUP_TKIP;
+			else if (!strcmp (cipher, "ccmp"))
+				flags |= NM_802_11_AP_SEC_GROUP_CCMP;
+		}
+	}
+
+	if (has_proto (sec, PROTO_WPA))
+		nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags);
+	if (has_proto (sec, PROTO_RSN))
+		nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags);
+}
+
+NMAccessPoint *
+nm_ap_new_fake_from_connection (NMConnection *connection)
+{
+	NMAccessPoint *ap;
+	NMSettingWireless *s_wireless;
+	NMSettingWirelessSecurity *s_wireless_sec;
+	const GByteArray *ssid;
+	const char *mode, *band, *key_mgmt;
+	guint32 channel, flags;
+	gboolean psk = FALSE, eap = FALSE;
+
+	g_return_val_if_fail (connection != NULL, NULL);
+
+	s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS));
+	g_return_val_if_fail (s_wireless != NULL, NULL);
+
+	ssid = nm_setting_wireless_get_ssid (s_wireless);
+	g_return_val_if_fail (ssid != NULL, NULL);
+	g_return_val_if_fail (ssid->len > 0, NULL);
+
+	ap = nm_ap_new ();
+	nm_ap_set_fake (ap, TRUE);
+	nm_ap_set_ssid (ap, ssid);
+
+	// FIXME: bssid too?
+
+	mode = nm_setting_wireless_get_mode (s_wireless);
+	if (mode) {
+		if (!strcmp (mode, "infrastructure"))
+			nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+		else if (!strcmp (mode, "adhoc"))
+			nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
+		else
+			goto error;
+	} else {
+		nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+	}
+
+	band = nm_setting_wireless_get_band (s_wireless);
+	channel = nm_setting_wireless_get_channel (s_wireless);
+
+	if (band && channel) {
+		guint32 freq = channel_to_freq (channel, band);
+
+		if (freq == 0)
+			goto error;
+
+		nm_ap_set_freq (ap, freq);
+	}
+
+	s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
+	/* Assume presence of a security setting means the AP is encrypted */
+	if (!s_wireless_sec)
+		goto done;
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec);
+
+	/* Everything below here uses encryption */
+	nm_ap_set_flags (ap, nm_ap_get_flags (ap) | NM_802_11_AP_FLAGS_PRIVACY);
+
+	/* Static & Dynamic WEP */
+	if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x"))
+		goto done;
+
+	psk = !strcmp (key_mgmt, "wpa-psk");
+	eap = !strcmp (key_mgmt, "wpa-eap");
+	if (psk || eap) {
+		if (has_proto (s_wireless_sec, PROTO_WPA)) {
+			flags = nm_ap_get_wpa_flags (ap);
+			flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK;
+			nm_ap_set_wpa_flags (ap, flags);
+		}
+		if (has_proto (s_wireless_sec, PROTO_RSN)) {
+			flags = nm_ap_get_rsn_flags (ap);
+			flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK;
+			nm_ap_set_rsn_flags (ap, flags);
+		}
+
+		add_pair_ciphers (ap, s_wireless_sec);
+		add_group_ciphers (ap, s_wireless_sec);
+	} else if (!strcmp (key_mgmt, "wpa-none")) {
+		guint32 i;
+
+		/* Ad-Hoc has special requirements: proto=WPA, pairwise=(none), and
+		 * group=TKIP/CCMP (but not both).
+		 */
+
+		flags = nm_ap_get_wpa_flags (ap);
+		flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
+
+		/* Clear ciphers; pairwise must be unset anyway, and group gets set below */
+		flags &= ~(  NM_802_11_AP_SEC_PAIR_WEP40
+		           | NM_802_11_AP_SEC_PAIR_WEP104
+		           | NM_802_11_AP_SEC_PAIR_TKIP
+		           | NM_802_11_AP_SEC_PAIR_CCMP
+		           | NM_802_11_AP_SEC_GROUP_WEP40
+		           | NM_802_11_AP_SEC_GROUP_WEP104
+		           | NM_802_11_AP_SEC_GROUP_TKIP
+		           | NM_802_11_AP_SEC_GROUP_CCMP);
+
+		for (i = 0; i < nm_setting_wireless_security_get_num_groups (s_wireless_sec); i++) {
+			if (!strcmp (nm_setting_wireless_security_get_group (s_wireless_sec, i), "ccmp")) {
+				flags |= NM_802_11_AP_SEC_GROUP_CCMP;
+				break;
+			}
+		}
+
+		/* Default to TKIP since not all WPA-capable cards can do CCMP */
+		if (!(flags & NM_802_11_AP_SEC_GROUP_CCMP))
+			flags |= NM_802_11_AP_SEC_GROUP_TKIP;
+
+		nm_ap_set_wpa_flags (ap, flags);
+
+		/* Don't use Ad-Hoc RSN yet */
+		nm_ap_set_rsn_flags (ap, NM_802_11_AP_SEC_NONE);
+	}
+
+done:
+	return ap;
+
+error:
+	g_object_unref (ap);
+	return NULL;
+}
+
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5]
+
+void
+nm_ap_print_self (NMAccessPoint *ap,
+                  const char * prefix)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (ap != NULL);
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	nm_log_dbg (LOGD_WIFI_SCAN, "%s'%s' (%p)",
+	            prefix,
+	            priv->ssid ? nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len) : "(none)",
+	            ap);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    BSSID     " MAC_FMT, MAC_ARG (priv->address.ether_addr_octet));
+	nm_log_dbg (LOGD_WIFI_SCAN, "    mode      %d", priv->mode);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    timestamp %ld", priv->timestamp.tv_sec);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    flags     0x%X", priv->flags);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    wpa flags 0x%X", priv->wpa_flags);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    rsn flags 0x%X", priv->rsn_flags);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    quality   %d", priv->strength);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    frequency %d", priv->freq);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    max rate  %d", priv->max_bitrate);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    last-seen %ld", priv->last_seen);
+}
+
+const char *
+nm_ap_get_dbus_path (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
+
+	return NM_AP_GET_PRIVATE (ap)->dbus_path;
+}
+
+
+/*
+ * Get/set functions for timestamp
+ *
+ */
+const GTimeVal *nm_ap_get_timestamp (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+
+	return (&NM_AP_GET_PRIVATE (ap)->timestamp);
+}
+
+void nm_ap_set_timestamp (NMAccessPoint *ap, glong sec, glong usec)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	priv->timestamp.tv_sec = sec;
+	priv->timestamp.tv_usec = usec;
+}
+
+void nm_ap_set_timestamp_via_timestamp (NMAccessPoint *ap, const GTimeVal *timestamp)
+{
+	g_return_if_fail (NM_IS_AP (ap));
+
+	NM_AP_GET_PRIVATE (ap)->timestamp = *timestamp;
+}
+
+/*
+ * Get/set functions for ssid
+ *
+ */
+const GByteArray * nm_ap_get_ssid (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
+
+	return NM_AP_GET_PRIVATE (ap)->ssid;
+}
+
+void
+nm_ap_set_ssid (NMAccessPoint *ap, const GByteArray * ssid)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if ((ssid == priv->ssid) && ssid == NULL)
+		return;
+
+	/* same SSID */
+	if ((ssid && priv->ssid) && (ssid->len == priv->ssid->len)) {
+		if (!memcmp (ssid->data, priv->ssid->data, ssid->len))
+			return;
+	}
+
+	if (priv->ssid) {
+		g_byte_array_free (priv->ssid, TRUE);
+		priv->ssid = NULL;
+	}
+
+	if (ssid) {
+		priv->ssid = g_byte_array_sized_new (ssid->len);
+		priv->ssid->len = ssid->len;
+		memcpy (priv->ssid->data, ssid->data, ssid->len);
+	}
+
+	g_object_notify (G_OBJECT (ap), NM_AP_SSID);
+}
+
+
+guint32
+nm_ap_get_flags (NMAccessPoint *ap)
+{
+	guint32 flags;
+
+	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_FLAGS_NONE);
+
+	g_object_get (ap, NM_AP_FLAGS, &flags, NULL);
+
+	return flags;
+}
+
+
+void
+nm_ap_set_flags (NMAccessPoint *ap, guint32 flags)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->flags != flags) {
+		priv->flags = flags;
+		g_object_notify (G_OBJECT (ap), NM_AP_FLAGS);
+	}
+}
+
+guint32
+nm_ap_get_wpa_flags (NMAccessPoint *ap)
+{
+	guint32 flags;
+
+	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE);
+
+	g_object_get (ap, NM_AP_WPA_FLAGS, &flags, NULL);
+
+	return flags;
+}
+
+
+void
+nm_ap_set_wpa_flags (NMAccessPoint *ap, guint32 flags)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->wpa_flags != flags) {
+		priv->wpa_flags = flags;
+		g_object_notify (G_OBJECT (ap), NM_AP_WPA_FLAGS);
+	}
+}
+
+guint32
+nm_ap_get_rsn_flags (NMAccessPoint *ap)
+{
+	guint32 flags;
+
+	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE);
+
+	g_object_get (ap, NM_AP_RSN_FLAGS, &flags, NULL);
+
+	return flags;
+}
+
+
+void
+nm_ap_set_rsn_flags (NMAccessPoint *ap, guint32 flags)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->rsn_flags != flags) {
+		priv->rsn_flags = flags;
+		g_object_notify (G_OBJECT (ap), NM_AP_RSN_FLAGS);
+	}
+}
+
+/*
+ * Get/set functions for address
+ *
+ */
+const struct ether_addr * nm_ap_get_address (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
+
+	return &NM_AP_GET_PRIVATE (ap)->address;
+}
+
+void nm_ap_set_address (NMAccessPoint *ap, const struct ether_addr * addr)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+	g_return_if_fail (addr != NULL);
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (memcmp (addr, &priv->address, sizeof (priv->address))) {
+		memcpy (&NM_AP_GET_PRIVATE (ap)->address, addr, sizeof (struct ether_addr));
+		g_object_notify (G_OBJECT (ap), NM_AP_HW_ADDRESS);
+	}
+}
+
+
+/*
+ * Get/set functions for mode (ie Ad-Hoc, Infrastructure, etc)
+ *
+ */
+NM80211Mode nm_ap_get_mode (NMAccessPoint *ap)
+{
+	NM80211Mode mode;
+
+	g_return_val_if_fail (NM_IS_AP (ap), -1);
+
+	g_object_get (ap, NM_AP_MODE, &mode, NULL);
+
+	return mode;
+}
+
+void nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	if (mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_INFRA) {
+		priv = NM_AP_GET_PRIVATE (ap);
+
+		if (priv->mode != mode) {
+			priv->mode = mode;
+			g_object_notify (G_OBJECT (ap), NM_AP_MODE);
+		}
+	} else
+		nm_log_warn (LOGD_WIFI, "Invalid AP mode '%d'", mode);
+}
+
+
+/*
+ * Get/set functions for strength
+ *
+ */
+gint8 nm_ap_get_strength (NMAccessPoint *ap)
+{
+	gint8 strength;
+
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+
+	g_object_get (ap, NM_AP_STRENGTH, &strength, NULL);
+
+	return strength;
+}
+
+void nm_ap_set_strength (NMAccessPoint *ap, const gint8 strength)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->strength != strength) {
+		priv->strength = strength;
+		g_object_notify (G_OBJECT (ap), NM_AP_STRENGTH);
+	}
+}
+
+
+/*
+ * Get/set functions for frequency
+ *
+ */
+guint32
+nm_ap_get_freq (NMAccessPoint *ap)
+{
+	guint32 freq;
+
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+
+	g_object_get (ap, NM_AP_FREQUENCY, &freq, NULL);
+
+	return freq;
+}
+
+void
+nm_ap_set_freq (NMAccessPoint *ap,
+                const guint32 freq)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->freq != freq) {
+		priv->freq = freq;
+		g_object_notify (G_OBJECT (ap), NM_AP_FREQUENCY);
+	}
+}
+
+
+/*
+ * Get/set functions for max bitrate
+ *
+ */
+guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap)
+{
+	guint32 rate;
+
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+
+	g_object_get (ap, NM_AP_MAX_BITRATE, &rate, NULL);
+
+	return rate;
+}
+
+void
+nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->max_bitrate != bitrate) {
+		priv->max_bitrate = bitrate;
+		g_object_notify (G_OBJECT (ap), NM_AP_MAX_BITRATE);
+	}
+}
+
+/*
+ * Get/Set functions to indicate that an access point is 'fake', ie whether
+ * or not it was created from scan results
+ */
+gboolean nm_ap_get_fake (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+
+	return NM_AP_GET_PRIVATE (ap)->fake;
+}
+
+void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake)
+{
+	g_return_if_fail (NM_IS_AP (ap));
+
+	NM_AP_GET_PRIVATE (ap)->fake = fake;
+}
+
+
+/*
+ * Get/Set functions to indicate whether an AP broadcasts its SSID.
+ */
+gboolean nm_ap_get_broadcast (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), TRUE);
+
+	return NM_AP_GET_PRIVATE (ap)->broadcast;
+}
+
+
+void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast)
+{
+	g_return_if_fail (NM_IS_AP (ap));
+
+	NM_AP_GET_PRIVATE (ap)->broadcast = broadcast;
+}
+
+
+/*
+ * Get/Set functions for how long ago the AP was last seen in a scan.
+ * APs older than a certain date are dropped from the list.
+ *
+ */
+glong nm_ap_get_last_seen (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+
+	return NM_AP_GET_PRIVATE (ap)->last_seen;
+}
+
+void nm_ap_set_last_seen (NMAccessPoint *ap, const glong last_seen)
+{
+	g_return_if_fail (NM_IS_AP (ap));
+
+	NM_AP_GET_PRIVATE (ap)->last_seen = last_seen;
+}
+
+
+/*
+ * Get/Set functions to indicate that an access point is
+ * user-created, ie whether or not its a network filled with
+ * information from the user and intended to create a new Ad-Hoc
+ * wireless network.
+ *
+ */
+gboolean nm_ap_get_user_created (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+
+	return NM_AP_GET_PRIVATE (ap)->user_created;
+}
+
+void nm_ap_set_user_created (NMAccessPoint *ap, gboolean user_created)
+{
+	g_return_if_fail (NM_IS_AP (ap));
+
+	NM_AP_GET_PRIVATE (ap)->user_created = user_created;
+}
+
+
+/*
+ * Get/Set functions for user address list
+ *
+ * The internal address list is always "owned" by the AP and
+ * the list returned by nm_ap_get_user_addresses() is a deep copy.
+ * Likewise, when setting the list, a deep copy is made for the
+ * ap's actual list.
+ *
+ */
+GSList *nm_ap_get_user_addresses (const NMAccessPoint *ap)
+{
+	GSList	*new = NULL;
+	GSList	*elt = NULL;
+
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
+
+	for (elt = NM_AP_GET_PRIVATE (ap)->user_addresses; elt; elt = g_slist_next (elt))
+	{
+		if (elt->data)
+			new = g_slist_append (new, g_strdup (elt->data));
+	}
+
+	/* Return a _deep__copy_ of the address list */
+	return new;
+}
+
+void nm_ap_set_user_addresses (NMAccessPoint *ap, GSList *list)
+{
+	NMAccessPointPrivate *priv;
+	GSList	*elt = NULL;
+	GSList	*new = NULL;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	/* Free existing list */
+	g_slist_foreach (priv->user_addresses, (GFunc) g_free, NULL);
+
+	/* Copy new list and set as our own */
+	for (elt = list; elt; elt = g_slist_next (elt))
+	{
+		if (elt->data)
+			new = g_slist_append (new, g_ascii_strup (elt->data, -1));
+	}
+
+	priv->user_addresses = new;
+}
+
+
+guint32
+nm_ap_add_security_from_ie (guint32 flags,
+                            const guint8 *wpa_ie,
+                            guint32 length)
+{
+	wpa_ie_data * cap_data;
+
+	if (!(cap_data = wpa_parse_wpa_ie (wpa_ie, length)))
+		return NM_802_11_AP_SEC_NONE;
+
+	/* Pairwise cipher flags */
+	if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_WEP40)
+		flags |= NM_802_11_AP_SEC_PAIR_WEP40;
+	if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_WEP104)
+		flags |= NM_802_11_AP_SEC_PAIR_WEP104;
+	if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_TKIP)
+		flags |= NM_802_11_AP_SEC_PAIR_TKIP;
+	if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_CCMP)
+		flags |= NM_802_11_AP_SEC_PAIR_CCMP;
+
+	/* Group cipher flags */
+	if (cap_data->group_cipher & IW_AUTH_CIPHER_WEP40)
+		flags |= NM_802_11_AP_SEC_GROUP_WEP40;
+	if (cap_data->group_cipher & IW_AUTH_CIPHER_WEP104)
+		flags |= NM_802_11_AP_SEC_GROUP_WEP104;
+	if (cap_data->group_cipher & IW_AUTH_CIPHER_TKIP)
+		flags |= NM_802_11_AP_SEC_GROUP_TKIP;
+	if (cap_data->group_cipher & IW_AUTH_CIPHER_CCMP)
+		flags |= NM_802_11_AP_SEC_GROUP_CCMP;
+
+	if (cap_data->key_mgmt & IW_AUTH_KEY_MGMT_802_1X)
+		flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+	if (cap_data->key_mgmt & IW_AUTH_KEY_MGMT_PSK)
+		flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
+
+	g_slice_free (wpa_ie_data, cap_data);
+	return flags;
+}
+
+gboolean
+nm_ap_check_compatible (NMAccessPoint *self,
+                        NMConnection *connection)
+{
+	NMAccessPointPrivate *priv;
+	NMSettingWireless *s_wireless;
+	NMSettingWirelessSecurity *s_wireless_sec;
+	const char *mode;
+	const char *band;
+	const GByteArray *bssid;
+	guint32 channel;
+
+	g_return_val_if_fail (NM_IS_AP (self), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+	priv = NM_AP_GET_PRIVATE (self);
+
+	s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS));
+	if (s_wireless == NULL)
+		return FALSE;
+	
+	if (!nm_utils_same_ssid (nm_setting_wireless_get_ssid (s_wireless), priv->ssid, TRUE))
+		return FALSE;
+
+	bssid = nm_setting_wireless_get_bssid (s_wireless);
+	if (bssid && memcmp (bssid->data, &priv->address, ETH_ALEN))
+		return FALSE;
+
+	mode = nm_setting_wireless_get_mode (s_wireless);
+	if (mode) {
+		if (!strcmp (mode, "infrastructure") && (priv->mode != NM_802_11_MODE_INFRA))
+			return FALSE;
+		if (!strcmp (mode, "adhoc") && (priv->mode != NM_802_11_MODE_ADHOC))
+			return FALSE;
+	}
+
+	band = nm_setting_wireless_get_band (s_wireless);
+	if (band) {
+		if (!strcmp (band, "a")) {
+			if (priv->freq < 4915 || priv->freq > 5825)
+				return FALSE;
+		} else if (!strcmp (band, "bg")) {
+			if (priv->freq < 2412 || priv->freq > 2484)
+				return FALSE;
+		}
+	}
+
+	channel = nm_setting_wireless_get_channel (s_wireless);
+	if (channel) {
+		guint32 ap_chan = freq_to_channel (priv->freq);
+
+		if (channel != ap_chan)
+			return FALSE;
+	}
+
+	s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection,
+															    NM_TYPE_SETTING_WIRELESS_SECURITY);
+
+	return nm_setting_wireless_ap_security_compatible (s_wireless,
+											 s_wireless_sec,
+											 nm_ap_get_flags (self),
+											 nm_ap_get_wpa_flags (self),
+											 nm_ap_get_rsn_flags (self),
+											 nm_ap_get_mode (self));
+}
+
+static gboolean
+capabilities_compatible (guint32 a_flags, guint32 b_flags)
+{
+	if (a_flags == b_flags)
+		return TRUE;
+
+	/* Make sure there's a common key management method */
+	if (!((a_flags & 0x300) & (b_flags & 0x300)))
+		return FALSE;
+
+	/* Ensure common pairwise ciphers */
+	if (!((a_flags & 0xF) & (b_flags & 0xF)))
+		return FALSE;
+
+	/* Ensure common group ciphers */
+	if (!((a_flags & 0xF0) & (b_flags & 0xF0)))
+		return FALSE;
+
+	return TRUE;
+}
+
+NMAccessPoint *
+nm_ap_match_in_list (NMAccessPoint *find_ap,
+                     GSList *ap_list,
+                     gboolean strict_match)
+{
+	GSList *iter;
+
+	g_return_val_if_fail (find_ap != NULL, NULL);
+
+	for (iter = ap_list; iter; iter = g_slist_next (iter)) {
+		NMAccessPoint * list_ap = NM_AP (iter->data);
+		const GByteArray * list_ssid = nm_ap_get_ssid (list_ap);
+		const struct ether_addr * list_addr = nm_ap_get_address (list_ap);
+
+		const GByteArray * find_ssid = nm_ap_get_ssid (find_ap);
+		const struct ether_addr * find_addr = nm_ap_get_address (find_ap);
+
+		/* SSID match; if both APs are hiding their SSIDs,
+		 * let matching continue on BSSID and other properties
+		 */
+		if (   (!list_ssid && find_ssid)
+		    || (list_ssid && !find_ssid)
+		    || !nm_utils_same_ssid (list_ssid, find_ssid, TRUE))
+			continue;
+
+		/* BSSID match */
+		if (   (strict_match || nm_ethernet_address_is_valid (find_addr))
+		    && nm_ethernet_address_is_valid (list_addr)
+		    && memcmp (list_addr->ether_addr_octet, 
+		               find_addr->ether_addr_octet,
+		               ETH_ALEN) != 0) {
+			continue;
+		}
+
+		/* mode match */
+		if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap))
+			continue;
+
+		/* Frequency match */
+		if (nm_ap_get_freq (list_ap) != nm_ap_get_freq (find_ap))
+			continue;
+
+		/* AP flags */
+		if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap))
+			continue;
+
+		if (strict_match) {
+			if (nm_ap_get_wpa_flags (list_ap) != nm_ap_get_wpa_flags (find_ap))
+				continue;
+
+			if (nm_ap_get_rsn_flags (list_ap) != nm_ap_get_rsn_flags (find_ap))
+				continue;
+		} else {
+			guint32 list_wpa_flags = nm_ap_get_wpa_flags (list_ap);
+			guint32 find_wpa_flags = nm_ap_get_wpa_flags (find_ap);
+			guint32 list_rsn_flags = nm_ap_get_rsn_flags (list_ap);
+			guint32 find_rsn_flags = nm_ap_get_rsn_flags (find_ap);
+
+			/* Just ensure that there is overlap in the capabilities */
+			if (   !capabilities_compatible (list_wpa_flags, find_wpa_flags)
+			    && !capabilities_compatible (list_rsn_flags, find_rsn_flags))
+				continue;
+		}
+
+		return list_ap;
+	}
+
+	return NULL;
+}
+
+
+struct cf_pair {
+	guint32 chan;
+	guint32 freq;
+};
+
+static struct cf_pair a_table[] = {
+	/* A band */
+	{  7, 5035 },
+	{  8, 5040 },
+	{  9, 5045 },
+	{ 11, 5055 },
+	{ 12, 5060 },
+	{ 16, 5080 },
+	{ 34, 5170 },
+	{ 36, 5180 },
+	{ 38, 5190 },
+	{ 40, 5200 },
+	{ 42, 5210 },
+	{ 44, 5220 },
+	{ 46, 5230 },
+	{ 48, 5240 },
+	{ 50, 5250 },
+	{ 52, 5260 },
+	{ 56, 5280 },
+	{ 58, 5290 },
+	{ 60, 5300 },
+	{ 64, 5320 },
+	{ 100, 5500 },
+	{ 104, 5520 },
+	{ 108, 5540 },
+	{ 112, 5560 },
+	{ 116, 5580 },
+	{ 120, 5600 },
+	{ 124, 5620 },
+	{ 128, 5640 },
+	{ 132, 5660 },
+	{ 136, 5680 },
+	{ 140, 5700 },
+	{ 149, 5745 },
+	{ 152, 5760 },
+	{ 153, 5765 },
+	{ 157, 5785 },
+	{ 160, 5800 },
+	{ 161, 5805 },
+	{ 165, 5825 },
+	{ 183, 4915 },
+	{ 184, 4920 },
+	{ 185, 4925 },
+	{ 187, 4935 },
+	{ 188, 4945 },
+	{ 192, 4960 },
+	{ 196, 4980 },
+	{ 0, -1 }
+};
+
+static struct cf_pair bg_table[] = {
+	/* B/G band */
+	{ 1, 2412 },
+	{ 2, 2417 },
+	{ 3, 2422 },
+	{ 4, 2427 },
+	{ 5, 2432 },
+	{ 6, 2437 },
+	{ 7, 2442 },
+	{ 8, 2447 },
+	{ 9, 2452 },
+	{ 10, 2457 },
+	{ 11, 2462 },
+	{ 12, 2467 },
+	{ 13, 2472 },
+	{ 14, 2484 },
+	{ 0, -1 }
+};
+
+guint32
+freq_to_channel (guint32 freq)
+{
+	int i = 0;
+
+	if (freq > 4900) {
+		while (a_table[i].chan && (a_table[i].freq != freq))
+			i++;
+		return a_table[i].chan;
+	} else {
+		while (bg_table[i].chan && (bg_table[i].freq != freq))
+			i++;
+		return bg_table[i].chan;
+	}
+
+	return 0;
+}
+
+guint32
+channel_to_freq (guint32 channel, const char *band)
+{
+	int i = 0;
+
+	if (!strcmp (band, "a")) {
+		while (a_table[i].chan && (a_table[i].chan != channel))
+			i++;
+		return a_table[i].freq;
+	} else if (!strcmp (band, "bg")) {
+		while (bg_table[i].chan && (bg_table[i].chan != channel))
+			i++;
+		return bg_table[i].freq;
+	}
+
+	return 0;
+}
+
diff --git a/src/nm-wifi-ap.h b/src/nm-wifi-ap.h
new file mode 100644
index 00000000..edc9e56b
--- /dev/null
+++ b/src/nm-wifi-ap.h
@@ -0,0 +1,129 @@
+/* -*- 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) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_ACCESS_POINT_H
+#define NM_ACCESS_POINT_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <time.h>
+#include "NetworkManager.h"
+#include "nm-connection.h"
+
+#define NM_TYPE_AP            (nm_ap_get_type ())
+#define NM_AP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AP, NMAccessPoint))
+#define NM_AP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AP, NMAccessPointClass))
+#define NM_IS_AP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_AP))
+#define NM_IS_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_AP))
+#define NM_AP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AP, NMAccessPointClass))
+
+#define NM_AP_FLAGS "flags"
+#define NM_AP_WPA_FLAGS "wpa-flags"
+#define NM_AP_RSN_FLAGS "rsn-flags"
+#define NM_AP_SSID "ssid"
+#define NM_AP_FREQUENCY "frequency"
+#define NM_AP_HW_ADDRESS "hw-address"
+#define NM_AP_MODE "mode"
+#define NM_AP_MAX_BITRATE "max-bitrate"
+#define NM_AP_STRENGTH "strength"
+
+typedef struct {
+	GObject parent;
+} NMAccessPoint;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*properties_changed) (NMAccessPoint *ap, GHashTable *properties);
+} NMAccessPointClass;
+
+GType nm_ap_get_type (void);
+
+NMAccessPoint *	nm_ap_new				(void);
+NMAccessPoint * nm_ap_new_from_properties (GHashTable *properties);
+NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection);
+void            nm_ap_export_to_dbus    (NMAccessPoint *ap);
+
+const char *		nm_ap_get_dbus_path (NMAccessPoint *ap);
+const GTimeVal *	nm_ap_get_timestamp				(const NMAccessPoint *ap);
+void				nm_ap_set_timestamp				(NMAccessPoint *ap, glong sec, glong usec);
+void				nm_ap_set_timestamp_via_timestamp	(NMAccessPoint *ap, const GTimeVal *timestamp);
+
+const GByteArray *	nm_ap_get_ssid (const NMAccessPoint * ap);
+void				nm_ap_set_ssid (NMAccessPoint * ap, const GByteArray * ssid);
+
+guint32			nm_ap_get_flags	(NMAccessPoint *ap);
+void				nm_ap_set_flags	(NMAccessPoint *ap, guint32 flags);
+
+guint32			nm_ap_get_wpa_flags	(NMAccessPoint *ap);
+void				nm_ap_set_wpa_flags	(NMAccessPoint *ap, guint32 flags);
+
+guint32			nm_ap_get_rsn_flags	(NMAccessPoint *ap);
+void				nm_ap_set_rsn_flags	(NMAccessPoint *ap, guint32 flags);
+
+const struct ether_addr * nm_ap_get_address	(const NMAccessPoint *ap);
+void				nm_ap_set_address		(NMAccessPoint *ap, const struct ether_addr *addr);
+
+NM80211Mode			nm_ap_get_mode			(NMAccessPoint *ap);
+void				nm_ap_set_mode			(NMAccessPoint *ap, const NM80211Mode mode);
+
+gint8			nm_ap_get_strength		(NMAccessPoint *ap);
+void				nm_ap_set_strength		(NMAccessPoint *ap, gint8 strength);
+
+guint32			nm_ap_get_freq			(NMAccessPoint *ap);
+void				nm_ap_set_freq			(NMAccessPoint *ap, guint32 freq);
+
+guint32			nm_ap_get_max_bitrate			(NMAccessPoint *ap);
+void				nm_ap_set_max_bitrate		(NMAccessPoint *ap, guint32 bitrate);
+
+gboolean			nm_ap_get_fake	(const NMAccessPoint *ap);
+void				nm_ap_set_fake	(NMAccessPoint *ap, gboolean fake);
+
+gboolean			nm_ap_get_broadcast		(NMAccessPoint *ap);
+void				nm_ap_set_broadcast		(NMAccessPoint *ap, gboolean broadcast);
+
+glong			nm_ap_get_last_seen		(const NMAccessPoint *ap);
+void				nm_ap_set_last_seen		(NMAccessPoint *ap, const glong last_seen);
+
+gboolean			nm_ap_get_user_created	(const NMAccessPoint *ap);
+void				nm_ap_set_user_created	(NMAccessPoint *ap, gboolean user_created);
+
+GSList *			nm_ap_get_user_addresses	(const NMAccessPoint *ap);
+void				nm_ap_set_user_addresses (NMAccessPoint *ap, GSList *list);
+
+guint32				nm_ap_add_security_from_ie (guint32 flags,
+                                                const guint8 *wpa_ie,
+                                                guint32 length);
+
+gboolean			nm_ap_check_compatible (NMAccessPoint *self,
+                                            NMConnection *connection);
+
+NMAccessPoint *     nm_ap_match_in_list (NMAccessPoint *find_ap,
+                                         GSList *ap_list,
+                                         gboolean strict_match);
+
+void				nm_ap_print_self (NMAccessPoint *ap, const char * prefix);
+
+guint32 freq_to_channel (guint32 freq);
+guint32 channel_to_freq (guint32 channel, const char *band);
+
+#endif /* NM_ACCESS_POINT_H */
diff --git a/src/ppp-manager/Makefile.am b/src/ppp-manager/Makefile.am
new file mode 100644
index 00000000..2dc7ad94
--- /dev/null
+++ b/src/ppp-manager/Makefile.am
@@ -0,0 +1,58 @@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_builddir}/marshallers
+
+noinst_LTLIBRARIES = libppp-manager.la
+
+libppp_manager_la_SOURCES = \
+	nm-ppp-manager.c \
+	nm-ppp-manager.h \
+	nm-ppp-status.h
+
+nm-ppp-manager-glue.h: $(top_srcdir)/introspection/nm-ppp-manager.xml
+	dbus-binding-tool --prefix=nm_ppp_manager --mode=glib-server --output=$@ $<
+
+built_sources = nm-ppp-manager-glue.h
+
+$(libppp_manager_la_OBJECTS): $(built_sources)
+
+libppp_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLIBDIR=\"$(libdir)\" \
+	-DPLUGINDIR=\"$(PPPD_PLUGIN_DIR)\"
+
+libppp_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+pppd_plugindir = $(PPPD_PLUGIN_DIR)
+pppd_plugin_LTLIBRARIES = nm-pppd-plugin.la
+
+nm_pppd_plugin_la_SOURCES = \
+	nm-pppd-plugin.c \
+	nm-pppd-plugin.h \
+	nm-ppp-status.h
+
+nm_pppd_plugin_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS)
+
+nm_pppd_plugin_la_LDFLAGS = -module -avoid-version
+
+nm_pppd_plugin_la_LIBADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+BUILT_SOURCES = nm-ppp-manager-glue.h
+
+CLEANFILES = $(BUILT_SOURCES)
+
diff --git a/src/ppp-manager/Makefile.in b/src/ppp-manager/Makefile.in
new file mode 100644
index 00000000..59cb32ba
--- /dev/null
+++ b/src/ppp-manager/Makefile.in
@@ -0,0 +1,679 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/ppp-manager
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(pppd_plugindir)"
+LTLIBRARIES = $(noinst_LTLIBRARIES) $(pppd_plugin_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libppp_manager_la_DEPENDENCIES =  \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libppp_manager_la_OBJECTS = libppp_manager_la-nm-ppp-manager.lo
+libppp_manager_la_OBJECTS = $(am_libppp_manager_la_OBJECTS)
+nm_pppd_plugin_la_DEPENDENCIES =  \
+	$(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+am_nm_pppd_plugin_la_OBJECTS = nm_pppd_plugin_la-nm-pppd-plugin.lo
+nm_pppd_plugin_la_OBJECTS = $(am_nm_pppd_plugin_la_OBJECTS)
+nm_pppd_plugin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(nm_pppd_plugin_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libppp_manager_la_SOURCES) $(nm_pppd_plugin_la_SOURCES)
+DIST_SOURCES = $(libppp_manager_la_SOURCES) \
+	$(nm_pppd_plugin_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_builddir}/marshallers
+
+noinst_LTLIBRARIES = libppp-manager.la
+libppp_manager_la_SOURCES = \
+	nm-ppp-manager.c \
+	nm-ppp-manager.h \
+	nm-ppp-status.h
+
+built_sources = nm-ppp-manager-glue.h
+libppp_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLIBDIR=\"$(libdir)\" \
+	-DPLUGINDIR=\"$(PPPD_PLUGIN_DIR)\"
+
+libppp_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+pppd_plugindir = $(PPPD_PLUGIN_DIR)
+pppd_plugin_LTLIBRARIES = nm-pppd-plugin.la
+nm_pppd_plugin_la_SOURCES = \
+	nm-pppd-plugin.c \
+	nm-pppd-plugin.h \
+	nm-ppp-status.h
+
+nm_pppd_plugin_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS)
+
+nm_pppd_plugin_la_LDFLAGS = -module -avoid-version
+nm_pppd_plugin_la_LIBADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+BUILT_SOURCES = nm-ppp-manager-glue.h
+CLEANFILES = $(BUILT_SOURCES)
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) 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/ppp-manager/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/ppp-manager/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+install-pppd_pluginLTLIBRARIES: $(pppd_plugin_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	test -z "$(pppd_plugindir)" || $(MKDIR_P) "$(DESTDIR)$(pppd_plugindir)"
+	@list='$(pppd_plugin_LTLIBRARIES)'; test -n "$(pppd_plugindir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pppd_plugindir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pppd_plugindir)"; \
+	}
+
+uninstall-pppd_pluginLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(pppd_plugin_LTLIBRARIES)'; test -n "$(pppd_plugindir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pppd_plugindir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pppd_plugindir)/$$f"; \
+	done
+
+clean-pppd_pluginLTLIBRARIES:
+	-test -z "$(pppd_plugin_LTLIBRARIES)" || rm -f $(pppd_plugin_LTLIBRARIES)
+	@list='$(pppd_plugin_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libppp-manager.la: $(libppp_manager_la_OBJECTS) $(libppp_manager_la_DEPENDENCIES) 
+	$(LINK)  $(libppp_manager_la_OBJECTS) $(libppp_manager_la_LIBADD) $(LIBS)
+nm-pppd-plugin.la: $(nm_pppd_plugin_la_OBJECTS) $(nm_pppd_plugin_la_DEPENDENCIES) 
+	$(nm_pppd_plugin_la_LINK) -rpath $(pppd_plugindir) $(nm_pppd_plugin_la_OBJECTS) $(nm_pppd_plugin_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libppp_manager_la-nm-ppp-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm_pppd_plugin_la-nm-pppd-plugin.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libppp_manager_la-nm-ppp-manager.lo: nm-ppp-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libppp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libppp_manager_la-nm-ppp-manager.lo -MD -MP -MF $(DEPDIR)/libppp_manager_la-nm-ppp-manager.Tpo -c -o libppp_manager_la-nm-ppp-manager.lo `test -f 'nm-ppp-manager.c' || echo '$(srcdir)/'`nm-ppp-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libppp_manager_la-nm-ppp-manager.Tpo $(DEPDIR)/libppp_manager_la-nm-ppp-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-ppp-manager.c' object='libppp_manager_la-nm-ppp-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libppp_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libppp_manager_la-nm-ppp-manager.lo `test -f 'nm-ppp-manager.c' || echo '$(srcdir)/'`nm-ppp-manager.c
+
+nm_pppd_plugin_la-nm-pppd-plugin.lo: nm-pppd-plugin.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_pppd_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm_pppd_plugin_la-nm-pppd-plugin.lo -MD -MP -MF $(DEPDIR)/nm_pppd_plugin_la-nm-pppd-plugin.Tpo -c -o nm_pppd_plugin_la-nm-pppd-plugin.lo `test -f 'nm-pppd-plugin.c' || echo '$(srcdir)/'`nm-pppd-plugin.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/nm_pppd_plugin_la-nm-pppd-plugin.Tpo $(DEPDIR)/nm_pppd_plugin_la-nm-pppd-plugin.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-pppd-plugin.c' object='nm_pppd_plugin_la-nm-pppd-plugin.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nm_pppd_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm_pppd_plugin_la-nm-pppd-plugin.lo `test -f 'nm-pppd-plugin.c' || echo '$(srcdir)/'`nm-pppd-plugin.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(pppd_plugindir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) 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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+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."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+	clean-pppd_pluginLTLIBRARIES 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-pppd_pluginLTLIBRARIES
+
+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: uninstall-pppd_pluginLTLIBRARIES
+
+.MAKE: all check install install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES \
+	clean-pppd_pluginLTLIBRARIES ctags 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-pppd_pluginLTLIBRARIES 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 uninstall \
+	uninstall-am uninstall-pppd_pluginLTLIBRARIES
+
+
+nm-ppp-manager-glue.h: $(top_srcdir)/introspection/nm-ppp-manager.xml
+	dbus-binding-tool --prefix=nm_ppp_manager --mode=glib-server --output=$@ $<
+
+$(libppp_manager_la_OBJECTS): $(built_sources)
+
+# 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/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c
new file mode 100644
index 00000000..148ad2cf
--- /dev/null
+++ b/src/ppp-manager/nm-ppp-manager.c
@@ -0,0 +1,1058 @@
+/* -*- 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) 2008 Novell, Inc.
+ * Copyright (C) 2008 - 2010 Red Hat, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <asm/types.h>
+#include <net/if.h>
+#include <sys/stat.h>
+
+#include <linux/ppp_defs.h>
+#ifndef aligned_u64
+#define aligned_u64 unsigned long long __attribute__((aligned(8)))
+#endif
+#include <linux/if_ppp.h>
+
+#include "NetworkManager.h"
+#include "nm-glib-compat.h"
+#include "nm-ppp-manager.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-ppp.h"
+#include "nm-setting-pppoe.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-cdma.h"
+#include "nm-dbus-manager.h"
+#include "nm-logging.h"
+#include "nm-marshal.h"
+
+static void impl_ppp_manager_need_secrets (NMPPPManager *manager,
+                                           DBusGMethodInvocation *context);
+
+static gboolean impl_ppp_manager_set_state (NMPPPManager *manager,
+								    guint32 state,
+								    GError **err);
+
+static gboolean impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
+									    GHashTable *config,
+									    GError **err);
+
+#include "nm-ppp-manager-glue.h"
+
+#define NM_PPPD_PLUGIN PLUGINDIR "/nm-pppd-plugin.so"
+#define PPP_MANAGER_SECRET_TRIES "ppp-manager-secret-tries"
+
+typedef struct {
+	GPid pid;
+	NMDBusManager *dbus_manager;
+	char *dbus_path;
+
+	char *parent_iface;
+
+	NMActRequest *act_req;
+	DBusGMethodInvocation *pending_secrets_context;
+
+	guint32 ppp_watch_id;
+	guint32 ppp_timeout_handler;
+
+	/* Monitoring */
+	char *ip_iface;
+	int monitor_fd;
+	guint monitor_id;
+} NMPPPManagerPrivate;
+
+#define NM_PPP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PPP_MANAGER, NMPPPManagerPrivate))
+
+G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, G_TYPE_OBJECT)
+
+enum {
+	STATE_CHANGED,
+	IP4_CONFIG,
+	STATS,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_0,
+	PROP_PARENT_IFACE,
+	LAST_PROP
+};
+
+typedef enum {
+	NM_PPP_MANAGER_ERROR_UNKOWN
+} NMPPPManagerError;
+
+GQuark
+nm_ppp_manager_error_quark (void)
+{
+	static GQuark quark;
+
+	if (!quark)
+		quark = g_quark_from_static_string ("nm_ppp_manager_error");
+
+	return quark;
+}
+
+static void
+nm_ppp_manager_init (NMPPPManager *manager)
+{
+}
+
+static GObject *
+constructor (GType type,
+		   guint n_construct_params,
+		   GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	NMPPPManagerPrivate *priv;
+	DBusGConnection *connection;
+	static guint32 counter = 0;
+
+	object = G_OBJECT_CLASS (nm_ppp_manager_parent_class)->constructor (type,
+	                                                                    n_construct_params,
+	                                                                    construct_params);
+	if (!object)
+		return NULL;
+
+	priv = NM_PPP_MANAGER_GET_PRIVATE (object);
+	priv->dbus_manager = nm_dbus_manager_get ();
+	if (!priv->dbus_manager) {
+		g_object_unref (object);
+		return NULL;
+	}
+
+	connection = nm_dbus_manager_get_connection (priv->dbus_manager);
+	priv->dbus_path = g_strdup_printf (NM_DBUS_PATH "/PPP/%d", counter++);
+	dbus_g_connection_register_g_object (connection, priv->dbus_path, object);
+
+	return object;
+}
+
+static void
+dispose (GObject *object)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (object);
+
+	nm_ppp_manager_stop (NM_PPP_MANAGER (object));
+
+	g_object_unref (priv->act_req);
+	g_object_unref (priv->dbus_manager);
+
+	G_OBJECT_CLASS (nm_ppp_manager_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (object);
+
+	g_free (priv->ip_iface);
+	g_free (priv->parent_iface);
+
+	G_OBJECT_CLASS (nm_ppp_manager_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PARENT_IFACE:
+		if (priv->parent_iface)
+			g_free (priv->parent_iface);
+		priv->parent_iface = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PARENT_IFACE:
+		g_value_set_string (value, priv->parent_iface);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_ppp_manager_class_init (NMPPPManagerClass *manager_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+	g_type_class_add_private (manager_class, sizeof (NMPPPManagerPrivate));
+
+	object_class->constructor = constructor;
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_PARENT_IFACE,
+		 g_param_spec_string (NM_PPP_MANAGER_PARENT_IFACE,
+							"ParentIface",
+							"Parent interface",
+							NULL,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/* signals */
+	signals[STATE_CHANGED] =
+		g_signal_new ("state-changed",
+				    G_OBJECT_CLASS_TYPE (object_class),
+				    G_SIGNAL_RUN_FIRST,
+				    G_STRUCT_OFFSET (NMPPPManagerClass, state_changed),
+				    NULL, NULL,
+				    g_cclosure_marshal_VOID__UINT,
+				    G_TYPE_NONE, 1,
+				    G_TYPE_UINT);
+
+	signals[IP4_CONFIG] =
+		g_signal_new ("ip4-config",
+				    G_OBJECT_CLASS_TYPE (object_class),
+				    G_SIGNAL_RUN_FIRST,
+				    G_STRUCT_OFFSET (NMPPPManagerClass, ip4_config),
+				    NULL, NULL,
+				    _nm_marshal_VOID__STRING_OBJECT,
+				    G_TYPE_NONE, 2,
+				    G_TYPE_STRING,
+				    G_TYPE_OBJECT);
+
+	signals[STATS] =
+		g_signal_new ("stats",
+				    G_OBJECT_CLASS_TYPE (object_class),
+				    G_SIGNAL_RUN_FIRST,
+				    G_STRUCT_OFFSET (NMPPPManagerClass, stats),
+				    NULL, NULL,
+				    _nm_marshal_VOID__UINT_UINT,
+				    G_TYPE_NONE, 2,
+				    G_TYPE_UINT, G_TYPE_UINT);
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class),
+							   &dbus_glib_nm_ppp_manager_object_info);
+}
+
+NMPPPManager *
+nm_ppp_manager_new (const char *iface)
+{
+	g_return_val_if_fail (iface != NULL, NULL);
+
+	return (NMPPPManager *) g_object_new (NM_TYPE_PPP_MANAGER,
+	                                      NM_PPP_MANAGER_PARENT_IFACE, iface,
+	                                      NULL);
+}
+
+/*******************************************/
+
+static gboolean
+monitor_cb (gpointer user_data)
+{
+	NMPPPManager *manager = NM_PPP_MANAGER (user_data);
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+	struct ifpppstatsreq req;
+
+	memset (&req, 0, sizeof (req));
+	req.stats_ptr = (caddr_t) &req.stats;
+
+	strncpy (req.ifr__name, priv->ip_iface, sizeof (req.ifr__name));
+	if (ioctl (priv->monitor_fd, SIOCGPPPSTATS, &req) < 0) {
+		nm_log_warn (LOGD_PPP, "could not read ppp stats: %s", strerror (errno));
+	} else {
+		g_signal_emit (manager, signals[STATS], 0, 
+		               req.stats.p.ppp_ibytes,
+		               req.stats.p.ppp_obytes);
+	}
+
+	return TRUE;
+}
+
+static void
+monitor_stats (NMPPPManager *manager)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+
+	priv->monitor_fd = socket (AF_INET, SOCK_DGRAM, 0);
+	if (priv->monitor_fd > 0)
+		priv->monitor_id = g_timeout_add_seconds (5, monitor_cb, manager);
+	else
+		nm_log_warn (LOGD_PPP, "could not monitor PPP stats: %s", strerror (errno));
+}
+
+/*******************************************/
+
+static void
+remove_timeout_handler (NMPPPManager *manager)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+	
+	if (priv->ppp_timeout_handler) {
+		g_source_remove (priv->ppp_timeout_handler);
+		priv->ppp_timeout_handler = 0;
+	}
+}
+
+static void
+impl_ppp_manager_need_secrets (NMPPPManager *manager,
+                               DBusGMethodInvocation *context)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	const char *connection_type;
+	const char *setting_name;
+	guint32 tries;
+	GPtrArray *hints = NULL;
+	const char *hint1 = NULL, *hint2 = NULL;
+
+	connection = nm_act_request_get_connection (priv->act_req);
+
+	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+	g_assert (s_con);
+
+	connection_type = nm_setting_connection_get_connection_type (s_con);
+	g_assert (connection_type);
+
+	nm_connection_clear_secrets (connection);
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	if (!setting_name) {
+		NMSetting *setting;
+
+		setting = nm_connection_get_setting_by_name (connection, connection_type);
+		if (setting) {
+			const char *username = NULL;
+			const char *password = NULL;
+
+			/* FIXME: push this down to the settings and keep PPP manager generic */
+			if (NM_IS_SETTING_PPPOE (setting)) {
+				username = nm_setting_pppoe_get_username (NM_SETTING_PPPOE (setting));
+				password = nm_setting_pppoe_get_password (NM_SETTING_PPPOE (setting));
+			} else if (NM_IS_SETTING_GSM (setting)) {
+				username = nm_setting_gsm_get_username (NM_SETTING_GSM (setting));
+				password = nm_setting_gsm_get_password (NM_SETTING_GSM (setting));
+			} else if (NM_IS_SETTING_CDMA (setting)) {
+				username = nm_setting_cdma_get_username (NM_SETTING_CDMA (setting));
+				password = nm_setting_cdma_get_password (NM_SETTING_CDMA (setting));
+			}
+
+			/* If secrets are not required, send the existing username and password
+			 * back to the PPP plugin immediately.
+			 */
+			priv->pending_secrets_context = context;
+			nm_ppp_manager_update_secrets (manager,
+			                               priv->parent_iface,
+			                               username ? username : "",
+			                               password ? password : "",
+			                               NULL);
+		} else {
+			GError *err = NULL;
+
+			g_set_error (&err, NM_PPP_MANAGER_ERROR, NM_PPP_MANAGER_ERROR_UNKOWN,
+			             "Missing type-specific setting; no secrets could be found.");
+			nm_log_warn (LOGD_PPP, "%s", err->message);
+			dbus_g_method_return_error (context, err);
+		}
+		return;
+	}
+
+	/* Extract hints */
+	if (hints) {
+		if (hints->len > 0)
+			hint1 = g_ptr_array_index (hints, 0);
+		if (hints->len > 1)
+			hint2 = g_ptr_array_index (hints, 1);
+	}
+
+	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES));
+	/* Only ask for completely new secrets after retrying them once; some PPP
+	 * servers (T-Mobile USA) appear to ask a few times when they actually don't
+	 * even care what you pass back.
+	 */
+	nm_act_request_get_secrets (priv->act_req,
+	                            setting_name,
+	                            tries > 1 ? TRUE : FALSE,
+	                            SECRETS_CALLER_PPP,
+	                            hint1,
+	                            hint2);
+	g_object_set_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES, GUINT_TO_POINTER (++tries));
+	priv->pending_secrets_context = context;
+
+	if (hints)
+		g_ptr_array_free (hints, TRUE);
+}
+
+static gboolean impl_ppp_manager_set_state (NMPPPManager *manager,
+								    guint32 state,
+								    GError **err)
+{
+	g_signal_emit (manager, signals[STATE_CHANGED], 0, state);
+
+	return TRUE;
+}
+
+static gboolean
+impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
+						   GHashTable *config_hash,
+						   GError **err)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+	NMConnection *connection;
+	NMSettingPPP *s_ppp;
+	NMIP4Config *config;
+	NMIP4Address *addr;
+	GValue *val;
+	int i;
+
+	nm_log_info (LOGD_PPP, "PPP manager(IP Config Get) reply received.");
+
+	remove_timeout_handler (manager);
+
+	config = nm_ip4_config_new ();
+	addr = nm_ip4_address_new ();
+	nm_ip4_address_set_prefix (addr, 32);
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_GATEWAY);
+	if (val) {
+		nm_ip4_address_set_gateway (addr, g_value_get_uint (val));
+		nm_ip4_config_set_ptp_address (config, g_value_get_uint (val));
+	}
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_ADDRESS);
+	if (val)
+		nm_ip4_address_set_address (addr, g_value_get_uint (val));
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_PREFIX);
+	if (val)
+		nm_ip4_address_set_prefix (addr, g_value_get_uint (val));
+
+	if (nm_ip4_address_get_address (addr) && nm_ip4_address_get_prefix (addr)) {
+		nm_ip4_config_take_address (config, addr);
+	} else {
+		nm_log_err (LOGD_PPP, "invalid IPv4 address received!");
+		nm_ip4_address_unref (addr);
+		goto out;
+	}
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_DNS);
+	if (val) {
+		GArray *dns = (GArray *) g_value_get_boxed (val);
+
+		for (i = 0; i < dns->len; i++)
+			nm_ip4_config_add_nameserver (config, g_array_index (dns, guint, i));
+	}
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_WINS);
+	if (val) {
+		GArray *wins = (GArray *) g_value_get_boxed (val);
+
+		for (i = 0; i < wins->len; i++)
+			nm_ip4_config_add_wins (config, g_array_index (wins, guint, i));
+	}
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_PPP_IP4_CONFIG_INTERFACE);
+	if (!val || !G_VALUE_HOLDS_STRING (val)) {
+		nm_log_err (LOGD_PPP, "no interface received!");
+		goto out;
+	}
+	priv->ip_iface = g_value_dup_string (val);
+
+	/* Got successful IP4 config; obviously the secrets worked */
+	connection = nm_act_request_get_connection (priv->act_req);
+	g_assert (connection);
+	g_object_set_data (G_OBJECT (connection), PPP_MANAGER_SECRET_TRIES, NULL);
+
+	/* Merge in custom MTU */
+	s_ppp = (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP);
+	if (s_ppp) {
+		guint32 mtu = nm_setting_ppp_get_mtu (s_ppp);
+
+		if (mtu)
+			nm_ip4_config_set_mtu (config, mtu);
+	}
+
+	/* Push the IP4 config up to the device */
+	g_signal_emit (manager, signals[IP4_CONFIG], 0, priv->ip_iface, config);
+
+	monitor_stats (manager);
+
+ out:
+	g_object_unref (config);
+
+	return TRUE;
+}
+
+/*******************************************/
+
+
+
+typedef struct {
+	GPtrArray *array;
+	GStringChunk *chunk;
+} NMCmdLine;
+
+static NMCmdLine *
+nm_cmd_line_new (void)
+{
+	NMCmdLine *cmd;
+
+	cmd = g_slice_new (NMCmdLine);
+	cmd->array = g_ptr_array_new ();
+	cmd->chunk = g_string_chunk_new (1024);
+
+	return cmd;
+}
+
+static void
+nm_cmd_line_destroy (NMCmdLine *cmd)
+{
+	g_ptr_array_free (cmd->array, TRUE);
+	g_string_chunk_free (cmd->chunk);
+	g_slice_free (NMCmdLine, cmd);
+}
+
+static char *
+nm_cmd_line_to_str (NMCmdLine *cmd)
+{
+	char *str;
+
+	g_ptr_array_add (cmd->array, NULL);
+	str = g_strjoinv (" ", (gchar **) cmd->array->pdata);
+	g_ptr_array_remove_index (cmd->array, cmd->array->len - 1);
+
+	return str;
+}
+
+static void
+nm_cmd_line_add_string (NMCmdLine *cmd, const char *str)
+{
+	g_ptr_array_add (cmd->array, g_string_chunk_insert (cmd->chunk, str));
+}
+
+static void
+nm_cmd_line_add_int (NMCmdLine *cmd, int i)
+{
+	char *str;
+
+	str = g_strdup_printf ("%d", i);
+	nm_cmd_line_add_string (cmd, str);
+	g_free (str);
+}
+
+/*******************************************/
+
+static inline const char *
+nm_find_pppd (void)
+{
+	static const char *pppd_binary_paths[] = {
+		"/usr/local/sbin/pppd",
+		"/usr/sbin/pppd",
+		"/sbin/pppd",
+		NULL
+	};
+
+	const char  **pppd_binary = pppd_binary_paths;
+
+	while (*pppd_binary != NULL) {
+		if (g_file_test (*pppd_binary, G_FILE_TEST_EXISTS))
+			break;
+		pppd_binary++;
+	}
+
+	return *pppd_binary;
+}
+
+static void
+ppp_exit_code (guint pppd_exit_status, GPid pid)
+{
+	const char *msg;
+
+	switch (pppd_exit_status) {
+	case  1: 
+		msg = "Fatal pppd error"; 
+		break;
+	case  2: 
+		msg = "pppd options error"; 
+		break;
+	case  3: 
+		msg = "No root priv error"; 
+		break;
+	case  4: 
+		msg = "No ppp module error"; 
+		break;
+	case  5: 
+		msg = "pppd received a signal"; 
+		break;
+	case  6: 
+		msg = "Serial port lock failed"; 
+		break;
+	case  7: 
+		msg = "Serial port open failed"; 
+		break;
+	case  8: 
+		msg = "Connect script failed"; 
+		break;
+	case  9: 
+		msg = "Pty program error"; 
+		break;
+	case 10: 
+		msg = "PPP negotiation failed"; 
+		break;
+	case 11: 
+		msg = "Peer didn't authenticatie itself"; 
+		break;
+	case 12: 
+		msg = "Link idle: Idle Seconds reached."; 
+		break;
+	case 13: 
+		msg = "Connect time limit reached."; 
+		break;
+	case 14: 
+		msg = "Callback negotiated, call should come back.";
+		break;
+	case 15: 
+		msg = "Lack of LCP echo responses"; 
+		break;
+	case 16: 
+		msg = "A modem hung up the phone"; 
+		break;
+	case 17: 
+		msg = "Loopback detected"; 
+		break;
+	case 18: 
+		msg = "The init script failed"; 
+		break;
+	case 19: 
+		msg = "Authentication error.\n"
+			"We failed to authenticate ourselves to the peer.\n"
+			"Maybe bad account or password?";
+		break;
+	default:
+		msg = "Unknown error";
+	}
+
+	nm_log_warn (LOGD_PPP, "pppd pid %d exited with error: %s", pid, msg);
+}
+
+static void
+ppp_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+	NMPPPManager *manager = NM_PPP_MANAGER (user_data);
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+	guint err;
+
+	g_assert (pid == priv->pid);
+
+	if (WIFEXITED (status)) {
+		err = WEXITSTATUS (status);
+		if (err != 0)
+			ppp_exit_code (err, priv->pid);
+	} else if (WIFSTOPPED (status)) {
+		nm_log_info (LOGD_PPP, "pppd pid %d stopped unexpectedly with signal %d", priv->pid, WSTOPSIG (status));
+	} else if (WIFSIGNALED (status)) {
+		nm_log_info (LOGD_PPP, "pppd pid %d died with signal %d", priv->pid, WTERMSIG (status));
+	} else
+		nm_log_info (LOGD_PPP, "pppd pid %d died from an unknown cause", priv->pid);
+
+	nm_log_dbg (LOGD_PPP, "pppd pid %d cleaned up", priv->pid);
+	priv->pid = 0;
+	g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_PPP_STATUS_DEAD);
+}
+
+static gboolean
+pppd_timed_out (gpointer data)
+{
+	NMPPPManager *manager = NM_PPP_MANAGER (data);
+
+	nm_log_warn (LOGD_PPP, "pppd timed out or didn't initialize our dbus module");
+	nm_ppp_manager_stop (manager);
+
+	g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_PPP_STATUS_DEAD);
+
+	return FALSE;
+}
+
+static NMCmdLine *
+create_pppd_cmd_line (NMPPPManager *self,
+                      NMSettingPPP *setting, 
+                      NMSettingPPPOE *pppoe,
+                      const char *ppp_name,
+                      GError **err)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self);
+	const char *ppp_binary;
+	NMCmdLine *cmd;
+	const char *ppp_debug;
+
+	ppp_binary = nm_find_pppd ();
+	if (!ppp_binary) {
+		g_set_error (err, NM_PPP_MANAGER_ERROR, NM_PPP_MANAGER_ERROR,
+				   "Could not find ppp binary.");
+		return NULL;
+	}
+
+	/* Create pppd command line */
+	cmd = nm_cmd_line_new ();
+	nm_cmd_line_add_string (cmd, ppp_binary);
+
+	nm_cmd_line_add_string (cmd, "nodetach");
+	nm_cmd_line_add_string (cmd, "lock");
+
+	/* NM handles setting the default route */
+	nm_cmd_line_add_string (cmd, "nodefaultroute");
+
+	ppp_debug = getenv ("NM_PPP_DEBUG");
+	if (ppp_debug)
+		nm_cmd_line_add_string (cmd, "debug");
+
+	if (ppp_name) {
+		nm_cmd_line_add_string (cmd, "user");
+		nm_cmd_line_add_string (cmd, ppp_name);
+	}
+
+	if (pppoe) {
+		char *dev_str;
+		const char *pppoe_service;
+
+		nm_cmd_line_add_string (cmd, "plugin");
+		nm_cmd_line_add_string (cmd, "rp-pppoe.so");
+
+		dev_str = g_strdup_printf ("nic-%s", priv->parent_iface);
+		nm_cmd_line_add_string (cmd, dev_str);
+		g_free (dev_str);
+
+		pppoe_service = nm_setting_pppoe_get_service (pppoe);
+		if (pppoe_service) {
+			nm_cmd_line_add_string (cmd, "rp_pppoe_service");
+			nm_cmd_line_add_string (cmd, pppoe_service);
+		}
+	} else {
+		nm_cmd_line_add_string (cmd, priv->parent_iface);
+		/* Don't send some random address as the local address */
+		nm_cmd_line_add_string (cmd, "noipdefault");
+	}
+
+	if (nm_setting_ppp_get_baud (setting))
+		nm_cmd_line_add_int (cmd, nm_setting_ppp_get_baud (setting));
+
+	/* noauth by default, because we certainly don't have any information
+	 * with which to verify anything the peer gives us if we ask it to
+	 * authenticate itself, which is what 'auth' really means.
+	 */
+	nm_cmd_line_add_string (cmd, "noauth");
+
+	if (nm_setting_ppp_get_refuse_eap (setting))
+		nm_cmd_line_add_string (cmd, "refuse-eap");
+	if (nm_setting_ppp_get_refuse_pap (setting))
+		nm_cmd_line_add_string (cmd, "refuse-pap");
+	if (nm_setting_ppp_get_refuse_chap (setting))
+		nm_cmd_line_add_string (cmd, "refuse-chap");
+	if (nm_setting_ppp_get_refuse_mschap (setting))
+		nm_cmd_line_add_string (cmd, "refuse-mschap");
+	if (nm_setting_ppp_get_refuse_mschapv2 (setting))
+		nm_cmd_line_add_string (cmd, "refuse-mschap-v2");
+	if (nm_setting_ppp_get_nobsdcomp (setting))
+		nm_cmd_line_add_string (cmd, "nobsdcomp");
+	if (nm_setting_ppp_get_nodeflate (setting))
+		nm_cmd_line_add_string (cmd, "nodeflate");
+	if (nm_setting_ppp_get_require_mppe (setting))
+		nm_cmd_line_add_string (cmd, "require-mppe");
+	if (nm_setting_ppp_get_require_mppe_128 (setting))
+		nm_cmd_line_add_string (cmd, "require-mppe-128");
+	if (nm_setting_ppp_get_mppe_stateful (setting))
+		nm_cmd_line_add_string (cmd, "mppe-stateful");
+	if (nm_setting_ppp_get_crtscts (setting))
+		nm_cmd_line_add_string (cmd, "crtscts");
+
+	/* Always ask for DNS, we don't have to use them if the connection
+	 * overrides the returned servers.
+	 */
+	nm_cmd_line_add_string (cmd, "usepeerdns");
+
+	if (nm_setting_ppp_get_mru (setting)) {
+		nm_cmd_line_add_string (cmd, "mru");
+		nm_cmd_line_add_int (cmd, nm_setting_ppp_get_mru (setting));
+	}
+
+	if (nm_setting_ppp_get_mtu (setting)) {
+		nm_cmd_line_add_string (cmd, "mtu");
+		nm_cmd_line_add_int (cmd, nm_setting_ppp_get_mtu (setting));
+	}
+
+	nm_cmd_line_add_string (cmd, "lcp-echo-failure");
+	nm_cmd_line_add_int (cmd, nm_setting_ppp_get_lcp_echo_failure (setting));
+
+	nm_cmd_line_add_string (cmd, "lcp-echo-interval");
+	nm_cmd_line_add_int (cmd, nm_setting_ppp_get_lcp_echo_interval (setting));
+
+	nm_cmd_line_add_string (cmd, "ipparam");
+	nm_cmd_line_add_string (cmd, priv->dbus_path);
+
+	nm_cmd_line_add_string (cmd, "plugin");
+	nm_cmd_line_add_string (cmd, NM_PPPD_PLUGIN);
+
+	return cmd;
+}
+
+static void
+pppd_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static void
+pppoe_fill_defaults (NMSettingPPP *setting)
+{
+	if (!nm_setting_ppp_get_mtu (setting))
+		g_object_set (setting, NM_SETTING_PPP_MTU, (guint32) 1492, NULL);
+
+	if (!nm_setting_ppp_get_mru (setting))
+		g_object_set (setting, NM_SETTING_PPP_MRU, (guint32) 1492, NULL);
+
+	if (!nm_setting_ppp_get_lcp_echo_interval (setting))
+		g_object_set (setting, NM_SETTING_PPP_LCP_ECHO_INTERVAL, (guint32) 20, NULL);
+
+	if (!nm_setting_ppp_get_lcp_echo_failure (setting))
+		g_object_set (setting, NM_SETTING_PPP_LCP_ECHO_FAILURE, (guint32) 3, NULL);
+
+	g_object_set (setting,
+			    NM_SETTING_PPP_NOAUTH, TRUE,
+			    NM_SETTING_PPP_NODEFLATE, TRUE,
+			    NULL);
+
+	/* FIXME: These commented settings should be set as well, update NMSettingPPP first. */
+#if 0
+	setting->noipdefault = TRUE;
+	setting->default_asyncmap = TRUE;
+	setting->defaultroute = TRUE;
+	setting->hide_password = TRUE;
+	setting->noaccomp = TRUE;
+	setting->nopcomp = TRUE;
+	setting->novj = TRUE;
+	setting->novjccomp = TRUE;
+#endif
+}
+
+gboolean
+nm_ppp_manager_start (NMPPPManager *manager,
+                      NMActRequest *req,
+                      const char *ppp_name,
+                      guint32 timeout_secs,
+                      GError **err)
+{
+	NMPPPManagerPrivate *priv;
+	NMConnection *connection;
+	NMSettingPPP *s_ppp;
+	gboolean s_ppp_created = FALSE;
+	NMSettingPPPOE *pppoe_setting;
+	NMCmdLine *ppp_cmd;
+	char *cmd_str;
+	struct stat st;
+	int ignored;
+
+	g_return_val_if_fail (NM_IS_PPP_MANAGER (manager), FALSE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+
+	priv->pid = 0;
+
+	/* Make sure /dev/ppp exists (bgo #533064) */
+	if (stat ("/dev/ppp", &st) || !S_ISCHR (st.st_mode))
+		ignored = system ("/sbin/modprobe ppp_generic");
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	s_ppp = (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP);
+	if (!s_ppp) {
+		/* If the PPP settings are all default we may not have a PPP setting yet,
+		 * so just make a default one here.
+		 */
+		s_ppp = NM_SETTING_PPP (nm_setting_ppp_new ());
+		s_ppp_created = TRUE;
+	}
+	
+	pppoe_setting = (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
+	if (pppoe_setting)
+		pppoe_fill_defaults (s_ppp);
+
+	ppp_cmd = create_pppd_cmd_line (manager, s_ppp, pppoe_setting, ppp_name, err);
+	if (!ppp_cmd)
+		goto out;
+
+	g_ptr_array_add (ppp_cmd->array, NULL);
+
+	nm_log_info (LOGD_PPP, "starting PPP connection");
+
+	cmd_str = nm_cmd_line_to_str (ppp_cmd);
+	nm_log_dbg (LOGD_PPP, "command line: %s", cmd_str);
+	g_free (cmd_str);
+
+	priv->pid = 0;
+	if (!g_spawn_async (NULL, (char **) ppp_cmd->array->pdata, NULL,
+					G_SPAWN_DO_NOT_REAP_CHILD,
+					pppd_child_setup,
+					NULL, &priv->pid, err)) {
+		goto out;
+	}
+
+	nm_log_info (LOGD_PPP, "pppd started with pid %d", priv->pid);
+
+	priv->ppp_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) ppp_watch_cb, manager);
+	priv->ppp_timeout_handler = g_timeout_add_seconds (timeout_secs, pppd_timed_out, manager);
+	priv->act_req = g_object_ref (req);
+
+ out:
+	if (s_ppp_created)
+		g_object_unref (s_ppp);
+
+	if (ppp_cmd)
+		nm_cmd_line_destroy (ppp_cmd);
+
+	return priv->pid > 0;
+}
+
+void
+nm_ppp_manager_update_secrets (NMPPPManager *manager,
+                               const char *device,
+                               const char *username,
+                               const char *password,
+                               const char *error_message)
+{
+	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+
+	g_return_if_fail (NM_IS_PPP_MANAGER (manager));
+	g_return_if_fail (device != NULL);
+	g_return_if_fail (priv->pending_secrets_context != NULL);
+
+	if (error_message) {
+		g_return_if_fail (username == NULL);
+		g_return_if_fail (password == NULL);
+	} else {
+		g_return_if_fail (username != NULL);
+		g_return_if_fail (password != NULL);
+	}
+
+	if (error_message) {
+		GError *err = NULL;
+
+		g_set_error (&err, NM_PPP_MANAGER_ERROR, NM_PPP_MANAGER_ERROR_UNKOWN, "%s", error_message);
+		nm_log_warn (LOGD_PPP, "%s", error_message);
+		dbus_g_method_return_error (priv->pending_secrets_context, err);
+		g_error_free (err);
+	} else {
+		/* This is sort of a hack but...
+		   pppd plugin only ever needs username and password.
+		   Passing the full connection there would mean some bloat:
+		   the plugin would need to link against libnm-util just to parse this.
+		   So instead, let's just send what it needs */
+
+		dbus_g_method_return (priv->pending_secrets_context, username, password);
+	}
+	priv->pending_secrets_context = NULL;
+}
+
+static gboolean
+ensure_killed (gpointer data)
+{
+	int pid = GPOINTER_TO_INT (data);
+
+	if (kill (pid, 0) == 0)
+		kill (pid, SIGKILL);
+
+	/* ensure the child is reaped */
+	nm_log_dbg (LOGD_PPP, "waiting for pppd pid %d to exit", pid);
+	waitpid (pid, NULL, 0);
+	nm_log_dbg (LOGD_PPP, "pppd pid %d cleaned up", pid);
+
+	return FALSE;
+}
+
+void
+nm_ppp_manager_stop (NMPPPManager *manager)
+{
+	NMPPPManagerPrivate *priv;
+
+	g_return_if_fail (NM_IS_PPP_MANAGER (manager));
+
+	priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+
+	if (priv->monitor_id) {
+		g_source_remove (priv->monitor_id);
+		priv->monitor_id = 0;
+	}
+
+	if (priv->monitor_fd) {
+		/* Get the stats one last time */
+		monitor_cb (manager);
+		close (priv->monitor_fd);
+		priv->monitor_fd = 0;
+	}
+
+	if (priv->ppp_timeout_handler) {
+		g_source_remove (priv->ppp_timeout_handler);
+		priv->ppp_timeout_handler = 0;
+	}
+
+	if (priv->ppp_watch_id) {
+		g_source_remove (priv->ppp_watch_id);
+		priv->ppp_watch_id = 0;
+	}
+
+	if (priv->pid) {
+		if (kill (priv->pid, SIGTERM) == 0)
+			g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
+		else {
+			kill (priv->pid, SIGKILL);
+
+			/* ensure the child is reaped */
+			nm_log_dbg (LOGD_PPP, "waiting for pppd pid %d to exit", priv->pid);
+			waitpid (priv->pid, NULL, 0);
+			nm_log_dbg (LOGD_PPP, "pppd pid %d cleaned up", priv->pid);
+		}
+
+		priv->pid = 0;
+	}
+}
diff --git a/src/ppp-manager/nm-ppp-manager.h b/src/ppp-manager/nm-ppp-manager.h
new file mode 100644
index 00000000..a0200973
--- /dev/null
+++ b/src/ppp-manager/nm-ppp-manager.h
@@ -0,0 +1,80 @@
+/* -*- 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) 2008 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_PPP_MANAGER_H
+#define NM_PPP_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-ppp-status.h"
+#include "nm-activation-request.h"
+#include "nm-connection.h"
+#include "nm-ip4-config.h"
+#include "nm-pppd-plugin.h"
+
+#define NM_TYPE_PPP_MANAGER            (nm_ppp_manager_get_type ())
+#define NM_PPP_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PPP_MANAGER, NMPPPManager))
+#define NM_PPP_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_PPP_MANAGER, NMPPPManagerClass))
+#define NM_IS_PPP_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_PPP_MANAGER))
+#define NM_IS_PPP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_PPP_MANAGER))
+#define NM_PPP_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_PPP_MANAGER, NMPPPManagerClass))
+
+#define NM_PPP_MANAGER_PARENT_IFACE "parent-iface"
+
+typedef struct {
+	GObject parent;
+} NMPPPManager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*state_changed) (NMPPPManager *manager, NMPPPStatus status);
+	void (*ip4_config) (NMPPPManager *manager, const char *iface, NMIP4Config *config);
+	void (*stats) (NMPPPManager *manager, guint32 in_bytes, guint32 out_bytes);
+} NMPPPManagerClass;
+
+GType nm_ppp_manager_get_type (void);
+
+NMPPPManager *nm_ppp_manager_new (const char *iface);
+
+gboolean nm_ppp_manager_start (NMPPPManager *manager,
+                               NMActRequest *req,
+                               const char *ppp_name,
+                               guint32 timeout_secs,
+                               GError **err);
+
+void     nm_ppp_manager_update_secrets (NMPPPManager *manager,
+                                        const char *device,
+                                        const char *username,
+                                        const char *password,
+                                        const char *error_message);
+
+void     nm_ppp_manager_stop  (NMPPPManager *manager);
+
+
+#define NM_PPP_MANAGER_ERROR nm_ppp_manager_error_quark()
+#define NM_TYPE_PPP_MANAGER_ERROR (nm_ppp_manager_error_get_type ()) 
+
+GQuark nm_ppp_manager_error_quark (void);
+
+#endif /* NM_PPP_MANAGER_H */
diff --git a/src/ppp-manager/nm-ppp-status.h b/src/ppp-manager/nm-ppp-status.h
new file mode 100644
index 00000000..cfc7c393
--- /dev/null
+++ b/src/ppp-manager/nm-ppp-status.h
@@ -0,0 +1,43 @@
+/* -*- 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) 2008 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_PPP_STATUS_H
+#define NM_PPP_STATUS_H
+
+typedef enum {
+	NM_PPP_STATUS_UNKNOWN,
+
+	NM_PPP_STATUS_DEAD,
+	NM_PPP_STATUS_INITIALIZE,
+	NM_PPP_STATUS_SERIALCONN,
+	NM_PPP_STATUS_DORMANT,
+	NM_PPP_STATUS_ESTABLISH,
+	NM_PPP_STATUS_AUTHENTICATE,
+	NM_PPP_STATUS_CALLBACK,
+	NM_PPP_STATUS_NETWORK,
+	NM_PPP_STATUS_RUNNING,
+	NM_PPP_STATUS_TERMINATE,
+	NM_PPP_STATUS_DISCONNECT,
+	NM_PPP_STATUS_HOLDOFF,
+	NM_PPP_STATUS_MASTER
+} NMPPPStatus;
+
+#endif /* NM_PPP_STATUS_H */
diff --git a/src/ppp-manager/nm-pppd-plugin.c b/src/ppp-manager/nm-pppd-plugin.c
new file mode 100644
index 00000000..c64bb87f
--- /dev/null
+++ b/src/ppp-manager/nm-pppd-plugin.c
@@ -0,0 +1,359 @@
+/* -*- 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) 2008 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <pppd/pppd.h>
+#include <pppd/fsm.h>
+#include <pppd/ipcp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+#include "NetworkManager.h"
+#include "nm-pppd-plugin.h"
+#include "nm-ppp-status.h"
+#include "nm-dbus-glib-types.h"
+
+int plugin_init (void);
+
+char pppd_version[] = VERSION;
+
+static DBusGProxy *proxy = NULL;
+
+static void
+nm_phasechange (void *data, int arg)
+{
+	NMPPPStatus ppp_status = NM_PPP_STATUS_UNKNOWN;
+	char *ppp_phase;
+
+	g_return_if_fail (DBUS_IS_G_PROXY (proxy));
+
+	switch (arg) {
+	case PHASE_DEAD:
+		ppp_status = NM_PPP_STATUS_DEAD;
+		ppp_phase = "dead";
+		break;
+	case PHASE_INITIALIZE:
+		ppp_status = NM_PPP_STATUS_INITIALIZE;
+		ppp_phase = "initialize";
+		break;
+	case PHASE_SERIALCONN:
+		ppp_status = NM_PPP_STATUS_SERIALCONN;
+		ppp_phase = "serial connection";
+		break;
+	case PHASE_DORMANT:
+		ppp_status = NM_PPP_STATUS_DORMANT;
+		ppp_phase = "dormant";
+		break;
+	case PHASE_ESTABLISH:
+		ppp_status = NM_PPP_STATUS_ESTABLISH;
+		ppp_phase = "establish";
+		break;
+	case PHASE_AUTHENTICATE:
+		ppp_status = NM_PPP_STATUS_AUTHENTICATE;
+		ppp_phase = "authenticate";
+		break;
+	case PHASE_CALLBACK:
+		ppp_status = NM_PPP_STATUS_CALLBACK;
+		ppp_phase = "callback";
+		break;
+	case PHASE_NETWORK:
+		ppp_status = NM_PPP_STATUS_NETWORK;
+		ppp_phase = "network";
+		break;
+	case PHASE_RUNNING:
+		ppp_status = NM_PPP_STATUS_RUNNING;
+		ppp_phase = "running";
+		break;
+	case PHASE_TERMINATE:
+		ppp_status = NM_PPP_STATUS_TERMINATE;
+		ppp_phase = "terminate";
+		break;
+	case PHASE_DISCONNECT:
+		ppp_status = NM_PPP_STATUS_DISCONNECT;
+		ppp_phase = "disconnect";
+		break;
+	case PHASE_HOLDOFF:
+		ppp_status = NM_PPP_STATUS_HOLDOFF;
+		ppp_phase = "holdoff";
+		break;
+	case PHASE_MASTER:
+		ppp_status = NM_PPP_STATUS_MASTER;
+		ppp_phase = "master";
+		break;
+
+	default:
+		ppp_phase = "unknown";
+		break;
+	}
+
+	g_message ("nm-ppp-plugin: (%s): status %d / phase '%s'",
+	           __func__,
+	           ppp_status,
+	           ppp_phase);
+
+	if (ppp_status != NM_PPP_STATUS_UNKNOWN) {
+		dbus_g_proxy_call_no_reply (proxy, "SetState",
+		                            G_TYPE_UINT, ppp_status, G_TYPE_INVALID,
+		                            G_TYPE_INVALID);
+	}
+}
+
+static GValue *
+str_to_gvalue (const char *str)
+{
+	GValue *val;
+
+	val = g_slice_new0 (GValue);
+	g_value_init (val, G_TYPE_STRING);
+	g_value_set_string (val, str);
+
+	return val;
+}
+
+static GValue *
+uint_to_gvalue (guint32 i)
+{
+	GValue *val;
+
+	val = g_slice_new0 (GValue);
+	g_value_init (val, G_TYPE_UINT);
+	g_value_set_uint (val, i);
+
+	return val;
+}
+
+static void
+value_destroy (gpointer data)
+{
+	GValue *val = (GValue *) data;
+
+	g_value_unset (val);
+	g_slice_free (GValue, val);
+}
+
+static void
+nm_ip_up (void *data, int arg)
+{
+	ipcp_options opts = ipcp_gotoptions[0];
+	ipcp_options peer_opts = ipcp_hisoptions[0];
+	GHashTable *hash;
+	GArray *array;
+	GValue *val;
+	guint32 pppd_made_up_address = htonl (0x0a404040 + ifunit);
+
+	g_return_if_fail (DBUS_IS_G_PROXY (proxy));
+
+	g_message ("nm-ppp-plugin: (%s): ip-up event", __func__);
+
+	if (!opts.ouraddr) {
+		g_warning ("nm-ppp-plugin: (%s): didn't receive an internal IP from pppd!", __func__);
+		nm_phasechange (NULL, PHASE_DEAD);
+		return;
+	}
+
+	hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+							NULL, value_destroy);
+
+	g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_INTERFACE, 
+					 str_to_gvalue (ifname));
+
+	g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_ADDRESS, 
+					 uint_to_gvalue (opts.ouraddr));
+
+	/* Prefer the peer options remote address first, _unless_ pppd made the
+	 * address up, at which point prefer the local options remote address,
+	 * and if that's not right, use the made-up address as a last resort.
+	 */
+	if (peer_opts.hisaddr && (peer_opts.hisaddr != pppd_made_up_address)) {
+		g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_GATEWAY, 
+		                     uint_to_gvalue (peer_opts.hisaddr));
+	} else if (opts.hisaddr) {
+		g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_GATEWAY, 
+		                     uint_to_gvalue (opts.hisaddr));
+	} else if (peer_opts.hisaddr == pppd_made_up_address) {
+		/* As a last resort, use the made-up address */
+		g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_GATEWAY, 
+		                     uint_to_gvalue (peer_opts.hisaddr));
+	}
+
+	g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_PREFIX, uint_to_gvalue (32));
+
+	if (opts.dnsaddr[0] || opts.dnsaddr[1]) {
+		array = g_array_new (FALSE, FALSE, sizeof (guint32));
+
+		if (opts.dnsaddr[0])
+			g_array_append_val (array, opts.dnsaddr[0]);
+		if (opts.dnsaddr[1])
+			g_array_append_val (array, opts.dnsaddr[1]);
+
+		val = g_slice_new0 (GValue);
+		g_value_init (val, DBUS_TYPE_G_UINT_ARRAY);
+		g_value_set_boxed (val, array);
+
+		g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_DNS, val);
+	}
+
+	if (opts.winsaddr[0] || opts.winsaddr[1]) {
+		array = g_array_new (FALSE, FALSE, sizeof (guint32));
+
+		if (opts.winsaddr[0])
+			g_array_append_val (array, opts.winsaddr[0]);
+		if (opts.winsaddr[1])
+			g_array_append_val (array, opts.winsaddr[1]);
+
+		val = g_slice_new0 (GValue);
+		g_value_init (val, DBUS_TYPE_G_UINT_ARRAY);
+		g_value_set_boxed (val, array);
+
+		g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_WINS, val);
+	}
+
+	g_message ("nm-ppp-plugin: (%s): sending Ip4Config to NetworkManager...", __func__);
+
+	dbus_g_proxy_call_no_reply (proxy, "SetIp4Config",
+	                            DBUS_TYPE_G_MAP_OF_VARIANT, hash, G_TYPE_INVALID,
+	                            G_TYPE_INVALID);
+
+	g_hash_table_destroy (hash);
+}
+
+static int
+get_chap_check()
+{
+	return 1;
+}
+
+static int
+get_pap_check()
+{
+	return 1;
+}
+
+static int
+get_credentials (char *username, char *password)
+{
+	char *my_username = NULL;
+	char *my_password = NULL;
+	size_t len;
+	GError *err = NULL;
+
+	if (username && !password) {
+		/* pppd is checking pap support; return 1 for supported */
+		return 1;
+	}
+
+	g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), -1);
+
+	g_message ("nm-ppp-plugin: (%s): passwd-hook, requesting credentials...", __func__);
+
+	dbus_g_proxy_call (proxy, "NeedSecrets", &err,
+	                   G_TYPE_INVALID,
+	                   G_TYPE_STRING, &my_username,
+	                   G_TYPE_STRING, &my_password,
+	                   G_TYPE_INVALID);
+
+	if (err) {
+		g_warning ("nm-ppp-plugin: (%s): could not get secrets: (%d) %s",
+		           __func__,
+		           err ? err->code : -1,
+		           err->message ? err->message : "(unknown)");
+		g_error_free (err);
+		return -1;
+	}
+
+	g_message ("nm-ppp-plugin: (%s): got credentials from NetworkManager", __func__);
+
+	if (my_username) {
+		len = strlen (my_username) + 1;
+		len = len < MAXNAMELEN ? len : MAXNAMELEN;
+
+		strncpy (username, my_username, len);
+		username[len - 1] = '\0';
+
+		g_free (my_username);
+	}
+
+	if (my_password) {
+		len = strlen (my_password) + 1;
+		len = len < MAXSECRETLEN ? len : MAXSECRETLEN;
+
+		strncpy (password, my_password, len);
+		password[len - 1] = '\0';
+
+		g_free (my_password);
+	}
+
+	return 1;
+}
+
+static void
+nm_exit_notify (void *data, int arg)
+{
+	g_return_if_fail (DBUS_IS_G_PROXY (proxy));
+
+	g_message ("nm-ppp-plugin: (%s): cleaning up", __func__);
+
+	g_object_unref (proxy);
+	proxy = NULL;
+}
+
+int
+plugin_init (void)
+{
+	DBusGConnection *bus;
+	GError *err = NULL;
+
+	g_type_init ();
+
+	g_message ("nm-ppp-plugin: (%s): initializing", __func__);
+
+	bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
+	if (!bus) {
+		g_warning ("nm-pppd-plugin: (%s): couldn't connect to system bus: (%d) %s",
+		           __func__,
+		           err ? err->code : -1,
+		           err && err->message ? err->message : "(unknown)");
+		g_error_free (err);
+		return -1;
+	}
+
+	/* NM passes in the object path of the corresponding PPPManager
+	 * object as the 'ipparam' argument to pppd.
+	 */
+	proxy = dbus_g_proxy_new_for_name (bus, NM_DBUS_SERVICE, ipparam, NM_DBUS_INTERFACE_PPP);
+
+	dbus_g_connection_unref (bus);
+
+	chap_passwd_hook = get_credentials;
+	chap_check_hook = get_chap_check;
+	pap_passwd_hook = get_credentials;
+	pap_check_hook = get_pap_check;
+
+	add_notifier (&phasechange, nm_phasechange, NULL);
+	add_notifier (&ip_up_notifier, nm_ip_up, NULL);
+	add_notifier (&exitnotify, nm_exit_notify, proxy);
+
+	return 0;
+}
diff --git a/src/ppp-manager/nm-pppd-plugin.h b/src/ppp-manager/nm-pppd-plugin.h
new file mode 100644
index 00000000..95a2a182
--- /dev/null
+++ b/src/ppp-manager/nm-pppd-plugin.h
@@ -0,0 +1,29 @@
+/* -*- 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) 2008 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#define NM_DBUS_INTERFACE_PPP  "org.freedesktop.NetworkManager.PPP"
+
+#define NM_PPP_IP4_CONFIG_INTERFACE "interface"
+#define NM_PPP_IP4_CONFIG_ADDRESS "address"
+#define NM_PPP_IP4_CONFIG_PREFIX "prefix"
+#define NM_PPP_IP4_CONFIG_GATEWAY "gateway"
+#define NM_PPP_IP4_CONFIG_DNS "dns"
+#define NM_PPP_IP4_CONFIG_WINS "wins"
diff --git a/src/supplicant-manager/Makefile.am b/src/supplicant-manager/Makefile.am
new file mode 100644
index 00000000..e59ae843
--- /dev/null
+++ b/src/supplicant-manager/Makefile.am
@@ -0,0 +1,35 @@
+SUBDIRS=. tests
+
+INCLUDES = \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/marshallers \
+	-I${top_srcdir}/src/named-manager
+
+noinst_LTLIBRARIES = libsupplicant-manager.la
+
+libsupplicant_manager_la_SOURCES = \
+	nm-supplicant-types.h \
+	nm-supplicant-manager.h \
+	nm-supplicant-manager.c \
+	nm-supplicant-config.h \
+	nm-supplicant-config.c \
+	nm-supplicant-interface.c \
+	nm-supplicant-interface.h \
+	nm-supplicant-settings-verify.h \
+	nm-supplicant-settings-verify.c
+
+libsupplicant_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DNM_PKGDATADIR=\"$(pkgdatadir)\" \
+	-DNM_LOCALSTATEDIR=\"$(localstatedir)\"
+
+libsupplicant_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
diff --git a/src/supplicant-manager/Makefile.in b/src/supplicant-manager/Makefile.in
new file mode 100644
index 00000000..9d326ba7
--- /dev/null
+++ b/src/supplicant-manager/Makefile.in
@@ -0,0 +1,760 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/supplicant-manager
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libsupplicant_manager_la_DEPENDENCIES =  \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libsupplicant_manager_la_OBJECTS =  \
+	libsupplicant_manager_la-nm-supplicant-manager.lo \
+	libsupplicant_manager_la-nm-supplicant-config.lo \
+	libsupplicant_manager_la-nm-supplicant-interface.lo \
+	libsupplicant_manager_la-nm-supplicant-settings-verify.lo
+libsupplicant_manager_la_OBJECTS =  \
+	$(am_libsupplicant_manager_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libsupplicant_manager_la_SOURCES)
+DIST_SOURCES = $(libsupplicant_manager_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-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 uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+	$(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+	distdir
+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@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = . tests
+INCLUDES = \
+	-I${top_srcdir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/marshallers \
+	-I${top_srcdir}/src/named-manager
+
+noinst_LTLIBRARIES = libsupplicant-manager.la
+libsupplicant_manager_la_SOURCES = \
+	nm-supplicant-types.h \
+	nm-supplicant-manager.h \
+	nm-supplicant-manager.c \
+	nm-supplicant-config.h \
+	nm-supplicant-config.c \
+	nm-supplicant-interface.c \
+	nm-supplicant-interface.h \
+	nm-supplicant-settings-verify.h \
+	nm-supplicant-settings-verify.c
+
+libsupplicant_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	-DNM_PKGDATADIR=\"$(pkgdatadir)\" \
+	-DNM_LOCALSTATEDIR=\"$(localstatedir)\"
+
+libsupplicant_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+all: all-recursive
+
+.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/supplicant-manager/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/supplicant-manager/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libsupplicant-manager.la: $(libsupplicant_manager_la_OBJECTS) $(libsupplicant_manager_la_DEPENDENCIES) 
+	$(LINK)  $(libsupplicant_manager_la_OBJECTS) $(libsupplicant_manager_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsupplicant_manager_la-nm-supplicant-config.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsupplicant_manager_la-nm-supplicant-interface.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsupplicant_manager_la-nm-supplicant-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsupplicant_manager_la-nm-supplicant-settings-verify.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libsupplicant_manager_la-nm-supplicant-manager.lo: nm-supplicant-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsupplicant_manager_la-nm-supplicant-manager.lo -MD -MP -MF $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-manager.Tpo -c -o libsupplicant_manager_la-nm-supplicant-manager.lo `test -f 'nm-supplicant-manager.c' || echo '$(srcdir)/'`nm-supplicant-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-manager.Tpo $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-supplicant-manager.c' object='libsupplicant_manager_la-nm-supplicant-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsupplicant_manager_la-nm-supplicant-manager.lo `test -f 'nm-supplicant-manager.c' || echo '$(srcdir)/'`nm-supplicant-manager.c
+
+libsupplicant_manager_la-nm-supplicant-config.lo: nm-supplicant-config.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsupplicant_manager_la-nm-supplicant-config.lo -MD -MP -MF $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-config.Tpo -c -o libsupplicant_manager_la-nm-supplicant-config.lo `test -f 'nm-supplicant-config.c' || echo '$(srcdir)/'`nm-supplicant-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-config.Tpo $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-config.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-supplicant-config.c' object='libsupplicant_manager_la-nm-supplicant-config.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsupplicant_manager_la-nm-supplicant-config.lo `test -f 'nm-supplicant-config.c' || echo '$(srcdir)/'`nm-supplicant-config.c
+
+libsupplicant_manager_la-nm-supplicant-interface.lo: nm-supplicant-interface.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsupplicant_manager_la-nm-supplicant-interface.lo -MD -MP -MF $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-interface.Tpo -c -o libsupplicant_manager_la-nm-supplicant-interface.lo `test -f 'nm-supplicant-interface.c' || echo '$(srcdir)/'`nm-supplicant-interface.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-interface.Tpo $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-interface.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-supplicant-interface.c' object='libsupplicant_manager_la-nm-supplicant-interface.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsupplicant_manager_la-nm-supplicant-interface.lo `test -f 'nm-supplicant-interface.c' || echo '$(srcdir)/'`nm-supplicant-interface.c
+
+libsupplicant_manager_la-nm-supplicant-settings-verify.lo: nm-supplicant-settings-verify.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsupplicant_manager_la-nm-supplicant-settings-verify.lo -MD -MP -MF $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-settings-verify.Tpo -c -o libsupplicant_manager_la-nm-supplicant-settings-verify.lo `test -f 'nm-supplicant-settings-verify.c' || echo '$(srcdir)/'`nm-supplicant-settings-verify.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-settings-verify.Tpo $(DEPDIR)/libsupplicant_manager_la-nm-supplicant-settings-verify.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-supplicant-settings-verify.c' object='libsupplicant_manager_la-nm-supplicant-settings-verify.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsupplicant_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsupplicant_manager_la-nm-supplicant-settings-verify.lo `test -f 'nm-supplicant-settings-verify.c' || echo '$(srcdir)/'`nm-supplicant-settings-verify.c
+
+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.
+$(RECURSIVE_TARGETS):
+	@fail= failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	list='$(SUBDIRS)'; 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"
+
+$(RECURSIVE_CLEAN_TARGETS):
+	@fail= failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	rev=''; for subdir in $$list; do \
+	  if test "$$subdir" = "."; then :; else \
+	    rev="$$subdir $$rev"; \
+	  fi; \
+	done; \
+	rev="$$rev ."; \
+	target=`echo $@ | sed s/-recursive//`; \
+	for subdir in $$rev; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done && test -z "$$fail"
+tags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+	done
+ctags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+	done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	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; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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 \
+	    test -d "$(distdir)/$$subdir" \
+	    || $(MKDIR_P) "$(distdir)/$$subdir" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    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 $(LTLIBRARIES)
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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 clean-noinstLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile 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 -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \
+	install-am install-strip tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+	all all-am check check-am clean clean-generic clean-libtool \
+	clean-noinstLTLIBRARIES ctags ctags-recursive 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 \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \
+	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/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c
new file mode 100644
index 00000000..b516de52
--- /dev/null
+++ b/src/supplicant-manager/nm-supplicant-config.c
@@ -0,0 +1,1014 @@
+/* -*- 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) 2006 - 2010 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <netinet/ether.h>
+#include <dbus/dbus-glib.h>
+
+#include "nm-supplicant-config.h"
+#include "nm-supplicant-settings-verify.h"
+#include "nm-logging.h"
+#include "nm-setting.h"
+#include "NetworkManagerUtils.h"
+
+static char *hexstr2bin (const char *hex, size_t len);
+
+#define NM_SUPPLICANT_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                             NM_TYPE_SUPPLICANT_CONFIG, \
+                                             NMSupplicantConfigPrivate))
+
+G_DEFINE_TYPE (NMSupplicantConfig, nm_supplicant_config, G_TYPE_OBJECT)
+
+typedef struct {
+	char *value;
+	guint32 len;	
+	OptType type;
+} ConfigOption;
+
+typedef struct
+{
+	GHashTable *config;
+	GHashTable *blobs;
+	guint32    ap_scan;
+	gboolean   dispose_has_run;
+} NMSupplicantConfigPrivate;
+
+NMSupplicantConfig *
+nm_supplicant_config_new (void)
+{
+	return g_object_new (NM_TYPE_SUPPLICANT_CONFIG, NULL);
+}
+
+static void
+config_option_free (ConfigOption *opt)
+{
+	g_free (opt->value);
+	g_slice_free (ConfigOption, opt);
+}
+
+static void
+blob_free (GByteArray *array)
+{
+	g_byte_array_free (array, TRUE);
+}
+
+static void
+nm_supplicant_config_init (NMSupplicantConfig * self)
+{
+	NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
+
+	priv->config = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                      (GDestroyNotify) g_free,
+	                                      (GDestroyNotify) config_option_free);
+
+	priv->blobs = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                     (GDestroyNotify) g_free,
+	                                     (GDestroyNotify) blob_free);
+
+	priv->ap_scan = 1;
+	priv->dispose_has_run = FALSE;
+}
+
+static gboolean
+nm_supplicant_config_add_option_with_type (NMSupplicantConfig *self,
+                                           const char *key,
+                                           const char *value,
+                                           gint32 len,
+                                           OptType opt_type,
+                                           gboolean secret)
+{
+	NMSupplicantConfigPrivate *priv;
+	ConfigOption *old_opt;
+	ConfigOption *opt;
+	OptType type;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
+	g_return_val_if_fail (key != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+
+	priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
+
+	if (len < 0)
+		len = strlen (value);
+
+	if (opt_type != TYPE_INVALID)
+		type = opt_type;
+	else {
+		type = nm_supplicant_settings_verify_setting (key, value, len);
+		if (type == TYPE_INVALID) {
+			char buf[255];
+			memset (&buf[0], 0, sizeof (buf));
+			memcpy (&buf[0], value, len > 254 ? 254 : len);
+			nm_log_warn (LOGD_SUPPLICANT, "Key '%s' and/or value '%s' invalid.", key, buf);
+			return FALSE;
+		}
+	}
+
+	old_opt = (ConfigOption *) g_hash_table_lookup (priv->config, key);
+	if (old_opt) {
+		nm_log_warn (LOGD_SUPPLICANT, "Key '%s' already in table.", key);
+		return FALSE;
+	}
+
+	opt = g_slice_new0 (ConfigOption);
+	if (opt == NULL) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config option.");
+		return FALSE;
+	}
+
+	opt->value = g_malloc0 ((sizeof (char) * len) + 1);
+	if (opt->value == NULL) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config option value.");
+		g_slice_free (ConfigOption, opt);
+		return FALSE;
+	}
+	memcpy (opt->value, value, len);
+
+	opt->len = len;
+	opt->type = type;	
+
+	{
+		char buf[255];
+		memset (&buf[0], 0, sizeof (buf));
+		memcpy (&buf[0], opt->value, opt->len > 254 ? 254 : opt->len);
+		nm_log_info (LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, secret ? "<omitted>" : &buf[0]);
+	}
+
+	g_hash_table_insert (priv->config, g_strdup (key), opt);
+
+	return TRUE;
+}
+
+static gboolean
+nm_supplicant_config_add_option (NMSupplicantConfig *self,
+                                 const char *key,
+                                 const char *value,
+                                 gint32 len,
+                                 gboolean secret)
+{
+	return nm_supplicant_config_add_option_with_type (self, key, value, len, TYPE_INVALID, secret);
+}
+
+static gboolean
+nm_supplicant_config_add_blob (NMSupplicantConfig *self,
+                               const char *key,
+                               const GByteArray *value,
+                               const char *blobid)
+{
+	NMSupplicantConfigPrivate *priv;
+	ConfigOption *old_opt;
+	ConfigOption *opt;
+	OptType type;
+	GByteArray *blob;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
+	g_return_val_if_fail (key != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+	g_return_val_if_fail (value->len > 0, FALSE);
+	g_return_val_if_fail (blobid != NULL, FALSE);
+
+	priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
+
+	type = nm_supplicant_settings_verify_setting (key, (const char *) value->data, value->len);
+	if (type == TYPE_INVALID) {
+		nm_log_warn (LOGD_SUPPLICANT, "Key '%s' and/or it's contained value is invalid.", key);
+		return FALSE;
+	}
+
+	old_opt = (ConfigOption *) g_hash_table_lookup (priv->config, key);
+	if (old_opt) {
+		nm_log_warn (LOGD_SUPPLICANT, "Key '%s' already in table.", key);
+		return FALSE;
+	}
+
+	blob = g_byte_array_sized_new (value->len);
+	if (!blob) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config blob.");
+		return FALSE;
+	}
+	g_byte_array_append (blob, value->data, value->len);
+
+	opt = g_slice_new0 (ConfigOption);
+	if (opt == NULL) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config option.");
+		g_byte_array_free (blob, TRUE);
+		return FALSE;
+	}
+
+	opt->value = g_strdup_printf ("blob://%s", blobid);
+	if (opt->value == NULL) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't allocate memory for new config option value.");
+		g_byte_array_free (blob, TRUE);
+		g_slice_free (ConfigOption, opt);
+		return FALSE;
+	}
+
+	opt->len = strlen (opt->value);
+	opt->type = type;	
+
+	nm_log_info (LOGD_SUPPLICANT, "Config: added '%s' value '%s'", key, opt->value);
+
+	g_hash_table_insert (priv->config, g_strdup (key), opt);
+	g_hash_table_insert (priv->blobs, g_strdup (blobid), blob);
+
+	return TRUE;
+}
+
+static void
+nm_supplicant_config_finalize (GObject *object)
+{
+	/* Complete object destruction */
+	g_hash_table_destroy (NM_SUPPLICANT_CONFIG_GET_PRIVATE (object)->config);
+	g_hash_table_destroy (NM_SUPPLICANT_CONFIG_GET_PRIVATE (object)->blobs);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (nm_supplicant_config_parent_class)->finalize (object);
+}
+
+
+static void
+nm_supplicant_config_class_init (NMSupplicantConfigClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = nm_supplicant_config_finalize;
+
+	g_type_class_add_private (object_class, sizeof (NMSupplicantConfigPrivate));
+}
+
+guint32
+nm_supplicant_config_get_ap_scan (NMSupplicantConfig * self)
+{
+	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), 1);
+
+	return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan;
+}
+
+void
+nm_supplicant_config_set_ap_scan (NMSupplicantConfig * self,
+                                  guint32 ap_scan)
+{
+	g_return_if_fail (NM_IS_SUPPLICANT_CONFIG (self));
+	g_return_if_fail (ap_scan >= 0 && ap_scan <= 2);
+
+	NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan = ap_scan;
+}
+
+static void
+get_hash_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	ConfigOption *opt = (ConfigOption *) value;
+	GValue *variant;
+	GByteArray *array;
+
+	variant = g_slice_new0 (GValue);
+
+	switch (opt->type) {
+	case TYPE_INT:
+		g_value_init (variant, G_TYPE_INT);
+		g_value_set_int (variant, atoi (opt->value));
+		break;
+	case TYPE_BYTES:
+		array = g_byte_array_sized_new (opt->len);
+		g_byte_array_append (array, (const guint8 *) opt->value, opt->len);
+		g_value_init (variant, DBUS_TYPE_G_UCHAR_ARRAY);
+		g_value_set_boxed (variant, array);
+		g_byte_array_free (array, TRUE);
+		break;
+	case TYPE_KEYWORD:
+	case TYPE_STRING:
+		g_value_init (variant, G_TYPE_STRING);
+		g_value_set_string (variant, opt->value);
+		break;
+	default:
+		g_slice_free (GValue, variant);
+		return;
+	}
+
+	g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), variant);
+}
+
+static void
+destroy_hash_value (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+GHashTable *
+nm_supplicant_config_get_hash (NMSupplicantConfig * self)
+{
+	NMSupplicantConfigPrivate *priv;
+	GHashTable *hash;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), NULL);
+
+	hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                              (GDestroyNotify) g_free,
+	                              destroy_hash_value);
+
+	priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
+	g_hash_table_foreach (priv->config, get_hash_cb, hash);
+	return hash;
+}
+
+GHashTable *
+nm_supplicant_config_get_blobs (NMSupplicantConfig * self)
+{
+	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), NULL);
+
+	return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->blobs;
+}
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5]
+
+gboolean
+nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
+                                           NMSettingWireless * setting,
+                                           gboolean is_broadcast,
+                                           guint32 adhoc_freq,
+                                           gboolean has_scan_capa_ssid)
+{
+	NMSupplicantConfigPrivate *priv;
+	gboolean is_adhoc;
+	const char *mode;
+	const GByteArray *id;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
+	g_return_val_if_fail (setting != NULL, FALSE);
+
+	priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
+
+	mode = nm_setting_wireless_get_mode (setting);
+	is_adhoc = (mode && !strcmp (mode, "adhoc")) ? TRUE : FALSE;
+	if (is_adhoc)
+		priv->ap_scan = 2;
+	else if (is_broadcast == FALSE) {
+		/* drivers that support scanning specific SSIDs should use
+		 * ap_scan=1, while those that do not should use ap_scan=2.
+		 */
+		priv->ap_scan = has_scan_capa_ssid ? 1 : 2;
+	}
+
+	id = nm_setting_wireless_get_ssid (setting);
+	if (!nm_supplicant_config_add_option (self, "ssid", (char *) id->data, id->len, FALSE)) {
+		nm_log_warn (LOGD_SUPPLICANT, "Error adding SSID to supplicant config.");
+		return FALSE;
+	}
+
+	if (is_adhoc) {
+		if (!nm_supplicant_config_add_option (self, "mode", "1", -1, FALSE)) {
+			nm_log_warn (LOGD_SUPPLICANT, "Error adding mode to supplicant config.");
+			return FALSE;
+		}
+
+		if (adhoc_freq) {
+			char *str_freq;
+
+			str_freq = g_strdup_printf ("%u", adhoc_freq);
+			if (!nm_supplicant_config_add_option (self, "frequency", str_freq, -1, FALSE)) {
+				g_free (str_freq);
+				nm_log_warn (LOGD_SUPPLICANT, "Error adding Ad-Hoc frequency to supplicant config.");
+				return FALSE;
+			}
+			g_free (str_freq);
+		}
+	}
+
+	/* Except for Ad-Hoc networks, request that the driver probe for the
+	 * specific SSID we want to associate with.
+	 */
+	if (!is_adhoc) {
+		if (!nm_supplicant_config_add_option (self, "scan_ssid", "1", -1, FALSE))
+			return FALSE;
+	}
+
+	id = nm_setting_wireless_get_bssid (setting);
+	if (id && id->len) {
+		char *str_bssid;
+
+		str_bssid = g_strdup_printf (MAC_FMT, MAC_ARG (id->data));
+		if (!nm_supplicant_config_add_option (self, "bssid",
+		                                      str_bssid, strlen (str_bssid),
+		                                      FALSE)) {
+			g_free (str_bssid);
+			nm_log_warn (LOGD_SUPPLICANT, "Error adding BSSID to supplicant config.");
+			return FALSE;
+		}
+		g_free (str_bssid);
+	}
+
+	// FIXME: band & channel config items
+	
+	return TRUE;
+}
+
+static gboolean
+add_string_val (NMSupplicantConfig *self,
+                const char *field,
+                const char *name,
+                gboolean ucase,
+                gboolean secret)
+{
+	gboolean success;
+	char *value;
+
+	if (!field)
+		return TRUE;
+
+	value = ucase ? g_ascii_strup (field, -1) : g_strdup (field);
+	success = nm_supplicant_config_add_option (self, name, value, strlen (field), secret);
+	if (!success)
+		nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
+	g_free (value);
+	return success;
+}
+
+#define ADD_STRING_LIST_VAL(setting, setting_name, field, field_plural, name, ucase, secret) \
+	if (nm_setting_##setting_name##_get_num_##field_plural (setting)) { \
+		guint32 k; \
+		GString *str = g_string_new (NULL); \
+		for (k = 0; k < nm_setting_##setting_name##_get_num_##field_plural (setting); k++) { \
+			const char *item = nm_setting_##setting_name##_get_##field (setting, k); \
+			if (!str->len) { \
+				g_string_append (str, item); \
+			} else { \
+				g_string_append_c (str, ' '); \
+				g_string_append (str, item); \
+			} \
+		} \
+		if (ucase) \
+			g_string_ascii_up (str); \
+		if (str->len) \
+			success = nm_supplicant_config_add_option (self, name, str->str, -1, secret); \
+		else \
+			success = TRUE; \
+		g_string_free (str, TRUE); \
+		if (!success) { \
+			nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); \
+			return FALSE; \
+		} \
+	}
+
+static char *
+get_blob_id (const char *name, const char *seed_uid)
+{
+	char *uid = g_strdup_printf ("%s-%s", seed_uid, name);
+	char *p = uid;
+	while (*p) {
+		if (*p == '/') *p = '-';
+		p++;
+	}
+	return uid;
+}
+
+#define ADD_BLOB_VAL(field, name, con_uid) \
+	if (field && field->len) { \
+		char *uid = get_blob_id (name, con_uid); \
+		success = nm_supplicant_config_add_blob (self, name, field, uid); \
+		g_free (uid); \
+		if (!success) { \
+			nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name); \
+			return FALSE; \
+		} \
+	}
+
+
+static gboolean
+wep128_passphrase_hash (const char *input,
+                        size_t input_len,
+                        guint8 *out_digest,
+                        size_t *out_digest_len)
+{
+	GChecksum *sum;
+	guint8 data[64];
+	int i;
+
+	g_return_val_if_fail (out_digest != NULL, FALSE);
+	g_return_val_if_fail (out_digest_len != NULL, FALSE);
+	g_return_val_if_fail (*out_digest_len >= 16, FALSE);
+
+	/* Get at least 64 bytes by repeating the passphrase into the buffer */
+	for (i = 0; i < sizeof (data); i++)
+		data[i] = input[i % input_len];
+
+	sum = g_checksum_new (G_CHECKSUM_MD5);
+	g_assert (sum);
+	g_checksum_update (sum, data, sizeof (data));
+	g_checksum_get_digest (sum, out_digest, out_digest_len);
+	g_checksum_free (sum);
+
+	g_assert (*out_digest_len == 16);
+	/* WEP104 keys are 13 bytes in length (26 hex characters) */
+	*out_digest_len = 13;
+	return TRUE;
+}
+
+static gboolean
+add_wep_key (NMSupplicantConfig *self,
+             const char *key,
+             const char *name,
+             NMWepKeyType wep_type)
+{
+	char *value;
+	gboolean success;
+	size_t key_len = key ? strlen (key) : 0;
+
+	if (!key || !key_len)
+		return TRUE;
+
+	if (   (wep_type == NM_WEP_KEY_TYPE_UNKNOWN)
+	    || (wep_type == NM_WEP_KEY_TYPE_KEY)) {
+		if ((key_len == 10) || (key_len == 26)) {
+			value = hexstr2bin (key, strlen (key));
+			success = nm_supplicant_config_add_option (self, name, value, key_len / 2, TRUE);
+			g_free (value);
+			if (!success) {
+				nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
+				return FALSE;
+			}
+		} else if ((key_len == 5) || (key_len == 13)) {
+			if (!nm_supplicant_config_add_option (self, name, key, key_len, TRUE)) {
+				nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
+				return FALSE;
+			}
+		} else {
+			nm_log_warn (LOGD_SUPPLICANT, "Invalid WEP key '%s'", name);
+			return FALSE;
+		}
+	} else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) {
+		guint8 digest[16];
+		size_t digest_len = sizeof (digest);
+
+		success = wep128_passphrase_hash (key, key_len, digest, &digest_len);
+		if (success)
+			success = nm_supplicant_config_add_option (self, name, (const char *) digest, digest_len, TRUE);
+		if (!success) {
+			nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
+                                                    NMSettingWirelessSecurity *setting,
+                                                    NMSetting8021x *setting_8021x,
+                                                    const char *connection_uid)
+{
+	char *value;
+	gboolean success;
+	const char *key_mgmt, *auth_alg;
+	const char *psk;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
+	g_return_val_if_fail (setting != NULL, FALSE);
+	g_return_val_if_fail (connection_uid != NULL, FALSE);
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (setting);
+	if (!add_string_val (self, key_mgmt, "key_mgmt", TRUE, FALSE))
+		return FALSE;
+
+	auth_alg = nm_setting_wireless_security_get_auth_alg (setting);
+	if (!add_string_val (self, auth_alg, "auth_alg", TRUE, FALSE))
+		return FALSE;
+
+	psk = nm_setting_wireless_security_get_psk (setting);
+	if (psk) {
+		size_t psk_len = strlen (psk);
+
+		if (psk_len == 64) {
+			/* Hex PSK */
+			value = hexstr2bin (psk, psk_len);
+			success = nm_supplicant_config_add_option (self, "psk", value, psk_len / 2, TRUE);
+			g_free (value);
+			if (!success) {
+				nm_log_warn (LOGD_SUPPLICANT, "Error adding 'psk' to supplicant config.");
+				return FALSE;
+			}
+		} else if (psk_len >= 8 && psk_len <= 63) {
+			/* Use TYPE_STRING here so that it gets pushed to the
+			 * supplicant as a string, and therefore gets quoted,
+			 * and therefore the supplicant will interpret it as a
+			 * passphrase and not a hex key.
+			 */
+			if (!nm_supplicant_config_add_option_with_type (self, "psk", psk, -1, TYPE_STRING, TRUE)) {
+				nm_log_warn (LOGD_SUPPLICANT, "Error adding 'psk' to supplicant config.");
+				return FALSE;
+			}
+		} else {
+			/* Invalid PSK */
+			nm_log_warn (LOGD_SUPPLICANT, "Invalid PSK length %u: not between 8 and 63 characters inclusive.", (guint32) psk_len);
+			return FALSE;
+		}
+	}
+
+	/* Only WPA-specific things when using WPA */
+	if (   !strcmp (key_mgmt, "wpa-none")
+	    || !strcmp (key_mgmt, "wpa-psk")
+	    || !strcmp (key_mgmt, "wpa-eap")) {
+		ADD_STRING_LIST_VAL (setting, wireless_security, proto, protos, "proto", TRUE, FALSE);
+		ADD_STRING_LIST_VAL (setting, wireless_security, pairwise, pairwise, "pairwise", TRUE, FALSE);
+		ADD_STRING_LIST_VAL (setting, wireless_security, group, groups, "group", TRUE, FALSE);
+	}
+
+	/* WEP keys if required */
+	if (!strcmp (key_mgmt, "none")) {
+		NMWepKeyType wep_type = nm_setting_wireless_security_get_wep_key_type (setting);
+		const char *wep0 = nm_setting_wireless_security_get_wep_key (setting, 0);
+		const char *wep1 = nm_setting_wireless_security_get_wep_key (setting, 1);
+		const char *wep2 = nm_setting_wireless_security_get_wep_key (setting, 2);
+		const char *wep3 = nm_setting_wireless_security_get_wep_key (setting, 3);
+
+		if (!add_wep_key (self, wep0, "wep_key0", wep_type))
+			return FALSE;
+		if (!add_wep_key (self, wep1, "wep_key1", wep_type))
+			return FALSE;
+		if (!add_wep_key (self, wep2, "wep_key2", wep_type))
+			return FALSE;
+		if (!add_wep_key (self, wep3, "wep_key3", wep_type))
+			return FALSE;
+
+		if (wep0 || wep1 || wep2 || wep3) {
+			value = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (setting));
+			success = nm_supplicant_config_add_option (self, "wep_tx_keyidx", value, -1, FALSE);
+			g_free (value);
+			if (!success) {
+				nm_log_warn (LOGD_SUPPLICANT, "Error adding wep_tx_keyidx to supplicant config.");
+				return FALSE;
+			}
+		}
+	}
+
+	if (auth_alg && !strcmp (auth_alg, "leap")) {
+		/* LEAP */
+		if (!strcmp (key_mgmt, "ieee8021x")) {
+			const char *tmp;
+
+			tmp = nm_setting_wireless_security_get_leap_username (setting);
+			if (!add_string_val (self, tmp, "identity", FALSE, FALSE))
+				return FALSE;
+
+			tmp = nm_setting_wireless_security_get_leap_password (setting);
+			if (!add_string_val (self, tmp, "password", FALSE, TRUE))
+				return FALSE;
+
+			if (!add_string_val (self, "leap", "eap", TRUE, FALSE))
+				return FALSE;
+		} else {
+			return FALSE;
+		}
+	} else {
+		/* 802.1x for Dynamic WEP and WPA-Enterprise */
+		if (!strcmp (key_mgmt, "ieee8021x") || !strcmp (key_mgmt, "wpa-eap")) {
+		    if (!setting_8021x)
+		    	return FALSE;
+			if (!nm_supplicant_config_add_setting_8021x (self, setting_8021x, connection_uid, FALSE))
+				return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
+                                        NMSetting8021x *setting,
+                                        const char *connection_uid,
+                                        gboolean wired)
+{
+	char *tmp;
+	const char *peapver, *value, *path;
+	gboolean success, added;
+	GString *phase1, *phase2;
+	const GByteArray *array;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
+	g_return_val_if_fail (setting != NULL, FALSE);
+	g_return_val_if_fail (connection_uid != NULL, FALSE);
+
+	value = nm_setting_802_1x_get_password (setting);
+	if (!add_string_val (self, value, "password", FALSE, TRUE))
+		return FALSE;
+	value = nm_setting_802_1x_get_pin (setting);
+	if (!add_string_val (self, value, "pin", FALSE, TRUE))
+		return FALSE;
+
+	if (wired) {
+		if (!add_string_val (self, "IEEE8021X", "key_mgmt", FALSE, FALSE))
+			return FALSE;
+		/* Wired 802.1x must always use eapol_flags=0 */
+		if (!add_string_val (self, "0", "eapol_flags", FALSE, FALSE))
+			return FALSE;
+	}
+
+	ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", TRUE, FALSE);
+
+	/* Drop the fragment size a bit for better compatibility */
+	if (!nm_supplicant_config_add_option (self, "fragment_size", "1300", -1, FALSE))
+		return FALSE;
+
+	phase1 = g_string_new (NULL);
+	peapver = nm_setting_802_1x_get_phase1_peapver (setting);
+	if (peapver) {
+		if (!strcmp (peapver, "0"))
+			g_string_append (phase1, "peapver=0");
+		else if (!strcmp (peapver, "1"))
+			g_string_append (phase1, "peapver=1");
+	}
+
+	if (nm_setting_802_1x_get_phase1_peaplabel (setting)) {
+		if (phase1->len)
+			g_string_append_c (phase1, ' ');
+		g_string_append_printf (phase1, "peaplabel=%s", nm_setting_802_1x_get_phase1_peaplabel (setting));
+	}
+
+	if (phase1->len) {
+		if (!add_string_val (self, phase1->str, "phase1", FALSE, FALSE)) {
+			g_string_free (phase1, TRUE);
+			return FALSE;
+		}
+	}
+	g_string_free (phase1, TRUE);
+
+	phase2 = g_string_new (NULL);
+	if (nm_setting_802_1x_get_phase2_auth (setting)) {
+		tmp = g_ascii_strup (nm_setting_802_1x_get_phase2_auth (setting), -1);
+		g_string_append_printf (phase2, "auth=%s", tmp);
+		g_free (tmp);
+	}
+
+	if (nm_setting_802_1x_get_phase2_autheap (setting)) {
+		if (phase2->len)
+			g_string_append_c (phase2, ' ');
+		tmp = g_ascii_strup (nm_setting_802_1x_get_phase2_autheap (setting), -1);
+		g_string_append_printf (phase2, "autheap=%s", tmp);
+		g_free (tmp);
+	}
+
+	if (phase2->len) {
+		if (!add_string_val (self, phase2->str, "phase2", FALSE, FALSE)) {
+			g_string_free (phase2, TRUE);
+			return FALSE;
+		}
+	}
+	g_string_free (phase2, TRUE);
+
+	/* CA path */
+	path = nm_setting_802_1x_get_ca_path (setting);
+	if (nm_setting_802_1x_get_system_ca_certs (setting))
+		path = SYSTEM_CA_PATH;
+	if (path) {
+		if (!add_string_val (self, path, "ca_path", FALSE, FALSE))
+			return FALSE;
+	}
+
+	/* Phase2 CA path */
+	path = nm_setting_802_1x_get_phase2_ca_path (setting);
+	if (nm_setting_802_1x_get_system_ca_certs (setting))
+		path = SYSTEM_CA_PATH;
+	if (path) {
+		if (!add_string_val (self, path, "ca_path2", FALSE, FALSE))
+			return FALSE;
+	}
+
+	/* CA certificate */
+	switch (nm_setting_802_1x_get_ca_cert_scheme (setting)) {
+	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+		array = nm_setting_802_1x_get_ca_cert_blob (setting);
+		ADD_BLOB_VAL (array, "ca_cert", connection_uid);
+		break;
+	case NM_SETTING_802_1X_CK_SCHEME_PATH:
+		path = nm_setting_802_1x_get_ca_cert_path (setting);
+		if (!add_string_val (self, path, "ca_cert", FALSE, FALSE))
+			return FALSE;
+		break;
+	default:
+		break;
+	}
+
+	/* Phase 2 CA certificate */
+	switch (nm_setting_802_1x_get_phase2_ca_cert_scheme (setting)) {
+	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+		array = nm_setting_802_1x_get_phase2_ca_cert_blob (setting);
+		ADD_BLOB_VAL (array, "ca_cert2", connection_uid);
+		break;
+	case NM_SETTING_802_1X_CK_SCHEME_PATH:
+		path = nm_setting_802_1x_get_phase2_ca_cert_path (setting);
+		if (!add_string_val (self, path, "ca_cert2", FALSE, FALSE))
+			return FALSE;
+		break;
+	default:
+		break;
+	}
+
+	/* Private key */
+	added = FALSE;
+	switch (nm_setting_802_1x_get_private_key_scheme (setting)) {
+	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+		array = nm_setting_802_1x_get_private_key_blob (setting);
+		ADD_BLOB_VAL (array, "private_key", connection_uid);
+		added = TRUE;
+		break;
+	case NM_SETTING_802_1X_CK_SCHEME_PATH:
+		path = nm_setting_802_1x_get_private_key_path (setting);
+		if (!add_string_val (self, path, "private_key", FALSE, FALSE))
+			return FALSE;
+		added = TRUE;
+		break;
+	default:
+		break;
+	}
+
+	if (added) {
+		NMSetting8021xCKFormat format;
+		NMSetting8021xCKScheme scheme;
+
+		format = nm_setting_802_1x_get_private_key_format (setting);
+		scheme = nm_setting_802_1x_get_private_key_scheme (setting);
+
+		if (   scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
+		    || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
+			/* Only add the private key password for PKCS#12 blobs and
+			 * all path schemes, since in both of these cases the private key
+			 * isn't decrypted at all.
+			 */
+			value = nm_setting_802_1x_get_private_key_password (setting);
+			if (!add_string_val (self, value, "private_key_passwd", FALSE, TRUE))
+				return FALSE;
+		}
+
+		if (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
+			/* Only add the client cert if the private key is not PKCS#12, as
+			 * wpa_supplicant configuration directs us to do.
+			 */
+			switch (nm_setting_802_1x_get_client_cert_scheme (setting)) {
+			case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+				array = nm_setting_802_1x_get_client_cert_blob (setting);
+				ADD_BLOB_VAL (array, "client_cert", connection_uid);
+				break;
+			case NM_SETTING_802_1X_CK_SCHEME_PATH:
+				path = nm_setting_802_1x_get_client_cert_path (setting);
+				if (!add_string_val (self, path, "client_cert", FALSE, FALSE))
+					return FALSE;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	/* Phase 2 private key */
+	added = FALSE;
+	switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) {
+	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+		array = nm_setting_802_1x_get_phase2_private_key_blob (setting);
+		ADD_BLOB_VAL (array, "private_key2", connection_uid);
+		added = TRUE;
+		break;
+	case NM_SETTING_802_1X_CK_SCHEME_PATH:
+		path = nm_setting_802_1x_get_phase2_private_key_path (setting);
+		if (!add_string_val (self, path, "private_key2", FALSE, FALSE))
+			return FALSE;
+		added = TRUE;
+		break;
+	default:
+		break;
+	}
+
+	if (added) {
+		NMSetting8021xCKFormat format;
+		NMSetting8021xCKScheme scheme;
+
+		format = nm_setting_802_1x_get_phase2_private_key_format (setting);
+		scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting);
+
+		if (   scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
+		    || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
+			/* Only add the private key password for PKCS#12 blobs and
+			 * all path schemes, since in both of these cases the private key
+			 * isn't decrypted at all.
+			 */
+			value = nm_setting_802_1x_get_phase2_private_key_password (setting);
+			if (!add_string_val (self, value, "private_key_passwd2", FALSE, TRUE))
+				return FALSE;
+		}
+
+		if (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
+			/* Only add the client cert if the private key is not PKCS#12, as
+			 * wpa_supplicant configuration directs us to do.
+			 */
+			switch (nm_setting_802_1x_get_phase2_client_cert_scheme (setting)) {
+			case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+				array = nm_setting_802_1x_get_phase2_client_cert_blob (setting);
+				ADD_BLOB_VAL (array, "client_cert2", connection_uid);
+				break;
+			case NM_SETTING_802_1X_CK_SCHEME_PATH:
+				path = nm_setting_802_1x_get_phase2_client_cert_path (setting);
+				if (!add_string_val (self, path, "client_cert2", FALSE, FALSE))
+					return FALSE;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	value = nm_setting_802_1x_get_identity (setting);
+	if (!add_string_val (self, value, "identity", FALSE, FALSE))
+		return FALSE;
+	value = nm_setting_802_1x_get_anonymous_identity (setting);
+	if (!add_string_val (self, value, "anonymous_identity", FALSE, FALSE))
+		return FALSE;
+
+	return TRUE;
+}
+
+gboolean
+nm_supplicant_config_add_no_security (NMSupplicantConfig *self)
+{
+	return nm_supplicant_config_add_option (self, "key_mgmt", "NONE", -1, FALSE);
+}
+
+/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
+
+static int hex2num (char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	return -1;
+}
+
+static int hex2byte (const char *hex)
+{
+	int a, b;
+	a = hex2num(*hex++);
+	if (a < 0)
+		return -1;
+	b = hex2num(*hex++);
+	if (b < 0)
+		return -1;
+	return (a << 4) | b;
+}
+
+static char *
+hexstr2bin (const char *hex, size_t len)
+{
+	size_t       i;
+	int          a;
+	const char * ipos = hex;
+	char *       buf = NULL;
+	char *       opos;
+
+	/* Length must be a multiple of 2 */
+	if ((len % 2) != 0)
+		return NULL;
+
+	opos = buf = g_malloc0 ((len / 2) + 1);
+	for (i = 0; i < len; i += 2) {
+		a = hex2byte (ipos);
+		if (a < 0) {
+			g_free (buf);
+			return NULL;
+		}
+		*opos++ = a;
+		ipos += 2;
+	}
+	return buf;
+}
+
+/* End from hostap */
+
diff --git a/src/supplicant-manager/nm-supplicant-config.h b/src/supplicant-manager/nm-supplicant-config.h
new file mode 100644
index 00000000..dad23e29
--- /dev/null
+++ b/src/supplicant-manager/nm-supplicant-config.h
@@ -0,0 +1,84 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SUPPLICANT_CONFIG_H
+#define NM_SUPPLICANT_CONFIG_H
+
+#include <glib-object.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-wireless-security.h>
+#include <nm-setting-8021x.h>
+#include "nm-supplicant-types.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SUPPLICANT_CONFIG            (nm_supplicant_config_get_type ())
+#define NM_SUPPLICANT_CONFIG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfig))
+#define NM_SUPPLICANT_CONFIG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfigClass))
+#define NM_IS_SUPPLICANT_CONFIG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SUPPLICANT_CONFIG))
+#define NM_IS_SUPPLICANT_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_SUPPLICANT_CONFIG))
+#define NM_SUPPLICANT_CONFIG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfigClass))
+
+struct _NMSupplicantConfig
+{
+	GObject parent;
+};
+
+typedef struct
+{
+	GObjectClass parent;
+} NMSupplicantConfigClass;
+
+
+GType nm_supplicant_config_get_type (void);
+
+NMSupplicantConfig *nm_supplicant_config_new (void);
+
+guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self);
+
+void nm_supplicant_config_set_ap_scan (NMSupplicantConfig *self,
+                                       guint32 ap_scan);
+
+GHashTable *nm_supplicant_config_get_hash (NMSupplicantConfig *self);
+
+GHashTable *nm_supplicant_config_get_blobs (NMSupplicantConfig *self);
+
+gboolean nm_supplicant_config_add_setting_wireless (NMSupplicantConfig *self,
+                                                    NMSettingWireless *setting,
+                                                    gboolean is_broadcast,
+                                                    guint32 adhoc_freq,
+                                                    gboolean has_scan_capa_ssid);
+
+gboolean nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
+                                                             NMSettingWirelessSecurity *setting,
+                                                             NMSetting8021x *setting_8021x,
+                                                             const char *connection_uid);
+
+gboolean nm_supplicant_config_add_no_security (NMSupplicantConfig *self);
+
+gboolean nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
+                                                 NMSetting8021x *setting,
+                                                 const char *connection_uid,
+                                                 gboolean wired);
+
+G_END_DECLS
+
+#endif	/* NM_SUPPLICANT_CONFIG_H */
diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c
new file mode 100644
index 00000000..a65a458f
--- /dev/null
+++ b/src/supplicant-manager/nm-supplicant-interface.c
@@ -0,0 +1,1429 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#include "nm-supplicant-interface.h"
+#include "nm-supplicant-manager.h"
+#include "nm-logging.h"
+#include "nm-marshal.h"
+#include "nm-supplicant-config.h"
+#include "nm-dbus-manager.h"
+#include "nm-call-store.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+
+#define WPAS_DBUS_IFACE_INTERFACE   WPAS_DBUS_INTERFACE ".Interface"
+#define WPAS_DBUS_IFACE_BSSID       WPAS_DBUS_INTERFACE ".BSSID"
+#define WPAS_DBUS_IFACE_NETWORK	    WPAS_DBUS_INTERFACE ".Network"
+#define WPAS_ERROR_INVALID_IFACE    WPAS_DBUS_INTERFACE ".InvalidInterface"
+#define WPAS_ERROR_EXISTS_ERROR     WPAS_DBUS_INTERFACE ".ExistsError"
+
+
+G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT)
+
+
+#define NM_SUPPLICANT_INTERFACE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                                 NM_TYPE_SUPPLICANT_INTERFACE, \
+                                                 NMSupplicantInterfacePrivate))
+
+static void nm_supplicant_interface_set_property (GObject *      object,
+                                                  guint          prop_id,
+                                                  const GValue * value,
+                                                  GParamSpec *   pspec);
+
+static void nm_supplicant_interface_get_property (GObject *      object,
+                                                  guint          prop_id,
+                                                  GValue *       value,
+                                                  GParamSpec *   pspec);
+
+static void nm_supplicant_interface_start (NMSupplicantInterface *self);
+
+static void nm_supplicant_interface_add_to_supplicant (NMSupplicantInterface *self,
+                                                       gboolean get_only);
+
+static void nm_supplicant_interface_smgr_state_changed (NMSupplicantManager *smgr,
+                                                        guint32 new_state,
+                                                        guint32 old_state,
+                                                        gpointer user_data);
+
+static void nm_supplicant_interface_set_state (NMSupplicantInterface *self,
+                                               guint32 new_state);
+
+
+/* Signals */
+enum {
+	STATE,             /* change in the interface's state */
+	REMOVED,           /* interface was removed by the supplicant */
+	SCANNED_AP,        /* interface saw a new access point from a scan */
+	SCAN_REQ_RESULT,   /* result of a wireless scan request */
+	SCAN_RESULTS,      /* scan results returned from supplicant */
+	CONNECTION_STATE,  /* link state of the device's connection */
+	CONNECTION_ERROR,  /* an error occurred during a connection request */
+	LAST_SIGNAL
+};
+static guint nm_supplicant_interface_signals[LAST_SIGNAL] = { 0 };
+
+
+/* Properties */
+enum {
+	PROP_0 = 0,
+	PROP_SUPPLICANT_MANAGER,
+	PROP_DEVICE,
+	PROP_STATE,
+	PROP_CONNECTION_STATE,
+	PROP_SCANNING,
+	LAST_PROP
+};
+
+
+typedef struct
+{
+	NMSupplicantManager * smgr;
+	gulong                smgr_state_sig_handler;
+	NMDBusManager *       dbus_mgr;
+	char *                dev;
+	gboolean              is_wireless;
+
+	char *                object_path;
+	guint32               state;
+	NMCallStore *         assoc_pcalls;
+	NMCallStore *         other_pcalls;
+
+	guint32               con_state;
+	gboolean              scanning;
+
+	DBusGProxy *          iface_proxy;
+	DBusGProxy *          net_proxy;
+
+	guint                 scan_results_timeout;
+	guint32               last_scan;
+
+	NMSupplicantConfig *  cfg;
+
+	gboolean              dispose_has_run;
+} NMSupplicantInterfacePrivate;
+
+static gboolean
+cancel_all_cb (GObject *object, gpointer call_id, gpointer user_data)
+{
+	dbus_g_proxy_cancel_call (DBUS_G_PROXY (object), (DBusGProxyCall *) call_id);
+
+	return TRUE;
+}
+
+static void
+cancel_all_callbacks (NMCallStore *store)
+{
+	nm_call_store_foreach (store, NULL, cancel_all_cb, NULL);
+	nm_call_store_clear (store);
+}
+
+typedef struct {
+	NMSupplicantInterface *interface;
+	DBusGProxy *proxy;
+	NMCallStore *store;
+	DBusGProxyCall *call;
+	gboolean disposing;
+} NMSupplicantInfo;
+
+static NMSupplicantInfo *
+nm_supplicant_info_new (NMSupplicantInterface *interface,
+                        DBusGProxy *proxy,
+                        NMCallStore *store)
+{
+	NMSupplicantInfo *info;
+
+	info = g_slice_new0 (NMSupplicantInfo);
+	info->interface = g_object_ref (interface);
+	info->proxy = g_object_ref (proxy);
+	info->store = store;
+
+	return info;
+}
+
+static void
+nm_supplicant_info_set_call (NMSupplicantInfo *info, DBusGProxyCall *call)
+{
+	g_return_if_fail (info != NULL);
+	g_return_if_fail (call != NULL);
+
+	nm_call_store_add (info->store, G_OBJECT (info->proxy), (gpointer) call);
+	info->call = call;
+}
+
+static void
+nm_supplicant_info_destroy (gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+
+	/* Guard against double-disposal; since DBusGProxy doesn't guard against
+	 * double-disposal, we could infinite loop here if we're in the middle of
+	 * some wpa_supplicant D-Bus calls.  When the supplicant dies we'll dispose
+	 * of the proxy, which kills all its pending calls, which brings us here.
+	 * Then when we unref the proxy here, its dispose() function will get called
+	 * again, and we get right back here until we segfault because our callstack
+	 * is too long.
+	 */
+	if (!info->disposing) {
+		info->disposing = TRUE;
+
+		if (info->call) {
+			nm_call_store_remove (info->store, G_OBJECT (info->proxy), info->call);
+			info->call = NULL;
+		}
+
+		g_object_unref (info->proxy);
+		info->proxy = NULL;
+		g_object_unref (info->interface);
+		info->interface = NULL;
+
+		memset (info, 0, sizeof (NMSupplicantInfo));
+		g_slice_free (NMSupplicantInfo, info);
+	}
+}
+
+
+NMSupplicantInterface *
+nm_supplicant_interface_new (NMSupplicantManager * smgr, const char *ifname, gboolean is_wireless)
+{
+	NMSupplicantInterface * iface;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (smgr), NULL);
+	g_return_val_if_fail (ifname != NULL, NULL);
+
+	iface = g_object_new (NM_TYPE_SUPPLICANT_INTERFACE,
+	                      "supplicant-manager", smgr,
+	                      "device", ifname,
+	                      NULL);
+	if (iface) {
+		NM_SUPPLICANT_INTERFACE_GET_PRIVATE (iface)->is_wireless = is_wireless;
+		nm_supplicant_interface_start (iface);
+	}
+
+	return iface;
+}
+
+static void
+nm_supplicant_interface_init (NMSupplicantInterface * self)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
+	priv->con_state = NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED;
+	priv->assoc_pcalls = nm_call_store_new ();
+	priv->other_pcalls = nm_call_store_new ();
+
+	priv->dispose_has_run = FALSE;
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+}
+
+
+static void
+nm_supplicant_interface_set_property (GObject *      object,
+                                      guint          prop_id,
+                                      const GValue * value,
+                                      GParamSpec *   pspec)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
+	gulong id;
+
+	switch (prop_id) {
+		case PROP_SUPPLICANT_MANAGER:
+			priv->smgr = NM_SUPPLICANT_MANAGER (g_value_get_object (value));
+			g_object_ref (G_OBJECT (priv->smgr));
+			
+			id = g_signal_connect (priv->smgr,
+			                       "state",
+			                       G_CALLBACK (nm_supplicant_interface_smgr_state_changed),
+			                       object);
+			priv->smgr_state_sig_handler = id;
+			break;
+		case PROP_DEVICE:
+			/* Construct-only */
+			priv->dev = g_strdup (g_value_get_string (value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+nm_supplicant_interface_get_property (GObject *     object,
+                                      guint         prop_id,
+                                      GValue *      value,
+                                      GParamSpec *  pspec)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+		case PROP_SUPPLICANT_MANAGER:
+			g_value_set_object (value, G_OBJECT (priv->smgr));
+			break;
+		case PROP_DEVICE:
+			g_value_set_string (value, priv->dev);
+			break;
+		case PROP_STATE:
+			g_value_set_uint (value, priv->state);
+			break;
+		case PROP_CONNECTION_STATE:
+			g_value_set_uint (value, priv->con_state);
+			break;
+		case PROP_SCANNING:
+			g_value_set_boolean (value, priv->scanning);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+try_remove_iface (DBusGConnection *g_connection,
+                  const char *path)
+{
+	DBusGProxy *proxy;
+
+	g_return_if_fail (g_connection != NULL);
+	g_return_if_fail (path != NULL);
+
+	proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                   WPAS_DBUS_SERVICE,
+	                                   WPAS_DBUS_PATH,
+	                                   WPAS_DBUS_INTERFACE);
+	if (!proxy)
+		return;
+
+	dbus_g_proxy_call_no_reply (proxy, "removeInterface", 
+	                            DBUS_TYPE_G_OBJECT_PATH, path,
+	                            G_TYPE_INVALID);
+	g_object_unref (proxy);
+}
+
+static void
+nm_supplicant_interface_dispose (GObject *object)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
+	guint32 sm_state;
+
+	if (priv->dispose_has_run) {
+		G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
+		return;
+	}
+
+	priv->dispose_has_run = TRUE;
+
+	/* Ask wpa_supplicant to remove this interface */
+	sm_state = nm_supplicant_manager_get_state (priv->smgr);
+	if (sm_state == NM_SUPPLICANT_MANAGER_STATE_IDLE) {
+		if (priv->object_path) {
+			try_remove_iface (nm_dbus_manager_get_connection (priv->dbus_mgr),
+			                  priv->object_path);
+		}
+	}
+
+	if (priv->iface_proxy)
+		g_object_unref (priv->iface_proxy);
+
+	if (priv->net_proxy)
+		g_object_unref (priv->net_proxy);
+
+	if (priv->scan_results_timeout)
+		g_source_remove (priv->scan_results_timeout);
+
+	if (priv->smgr) {
+		g_signal_handler_disconnect (priv->smgr,
+		                             priv->smgr_state_sig_handler);
+		g_object_unref (priv->smgr);
+	}
+
+	g_free (priv->dev);
+
+	/* Cancel pending calls before unrefing the dbus manager */
+	cancel_all_callbacks (priv->other_pcalls);
+	nm_call_store_destroy (priv->other_pcalls);
+
+	cancel_all_callbacks (priv->assoc_pcalls);
+	nm_call_store_destroy (priv->assoc_pcalls);
+
+	if (priv->dbus_mgr)
+		g_object_unref (priv->dbus_mgr);
+
+	if (priv->cfg)
+		g_object_unref (priv->cfg);
+
+	g_free (priv->object_path);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
+}
+
+static void
+nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMSupplicantInterfacePrivate));
+
+	object_class->dispose = nm_supplicant_interface_dispose;
+	object_class->set_property = nm_supplicant_interface_set_property;
+	object_class->get_property = nm_supplicant_interface_get_property;
+
+	/* Properties */
+	g_object_class_install_property (object_class,
+	                                 PROP_SUPPLICANT_MANAGER,
+	                                 g_param_spec_object ("supplicant-manager",
+	                                                      "Supplicant Manager",
+	                                                      "Supplicant manager to which this interface belongs",
+	                                                      NM_TYPE_SUPPLICANT_MANAGER,
+	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  
+	g_object_class_install_property (object_class,
+	                                 PROP_DEVICE,
+	                                 g_param_spec_string ("device",
+	                                                      "Device",
+	                                                      "Device which this interface represents to the supplicant",
+	                                                      NULL,
+	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_STATE,
+	                                 g_param_spec_uint ("state",
+	                                                    "State",
+	                                                    "State of the supplicant interface; INIT, READY, or DOWN",
+	                                                    NM_SUPPLICANT_INTERFACE_STATE_INIT,
+	                                                    NM_SUPPLICANT_INTERFACE_STATE_LAST - 1,
+	                                                    NM_SUPPLICANT_INTERFACE_STATE_INIT,
+	                                                    G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_SCANNING,
+	                                 g_param_spec_boolean ("scanning",
+	                                                    "Scanning",
+	                                                    "Scanning",
+	                                                    FALSE,
+	                                                    G_PARAM_READABLE));
+
+	/* Signals */
+	nm_supplicant_interface_signals[STATE] =
+		g_signal_new ("state",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, state),
+		              NULL, NULL,
+		              _nm_marshal_VOID__UINT_UINT,
+		              G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+	nm_supplicant_interface_signals[REMOVED] =
+		g_signal_new ("removed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, removed),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__VOID,
+		              G_TYPE_NONE, 0);
+
+	nm_supplicant_interface_signals[SCANNED_AP] =
+		g_signal_new ("scanned-ap",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scanned_ap),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__POINTER,
+		              G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	nm_supplicant_interface_signals[SCAN_REQ_RESULT] =
+		g_signal_new ("scan-req-result",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scan_req_result),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__BOOLEAN,
+		              G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+	nm_supplicant_interface_signals[SCAN_RESULTS] =
+		g_signal_new ("scan-results",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scan_results),
+		              NULL, NULL,
+		              g_cclosure_marshal_VOID__UINT,
+		              G_TYPE_NONE, 1, G_TYPE_UINT);
+
+	nm_supplicant_interface_signals[CONNECTION_STATE] =
+		g_signal_new ("connection-state",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, connection_state),
+		              NULL, NULL,
+		              _nm_marshal_VOID__UINT_UINT,
+		              G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+	nm_supplicant_interface_signals[CONNECTION_ERROR] =
+		g_signal_new ("connection-error",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantInterfaceClass, connection_error),
+		              NULL, NULL,
+		              _nm_marshal_VOID__STRING_STRING,
+		              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+}
+
+static void
+emit_error_helper (NMSupplicantInterface *self,
+				   GError *err)
+{
+	const char *name = NULL;
+
+	if (err->domain == DBUS_GERROR && err->code == DBUS_GERROR_REMOTE_EXCEPTION)
+		name = dbus_g_error_get_name (err);
+
+	g_signal_emit (self,
+	               nm_supplicant_interface_signals[CONNECTION_ERROR],
+	               0,
+	               name,
+	               err->message);
+}
+
+static void
+bssid_properties_cb  (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	GError *err = NULL;
+	GHashTable *hash = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err,
+	                            DBUS_TYPE_G_MAP_OF_VARIANT, &hash,
+	                            G_TYPE_INVALID)) {
+		if (!strstr (err->message, "The BSSID requested was invalid")) {
+			nm_log_warn (LOGD_SUPPLICANT, "Couldn't retrieve BSSID properties: %s.",
+			             err->message);
+		}
+		g_error_free (err);
+	} else {
+		g_signal_emit (info->interface,
+		               nm_supplicant_interface_signals[SCANNED_AP],
+		               0,
+		               hash);
+
+		g_hash_table_destroy (hash);
+	}
+}
+
+static void
+request_bssid_properties (NMSupplicantInterface * self,
+                          const char * op)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	NMSupplicantInfo *info;
+	DBusGProxy *proxy;
+	DBusGProxyCall *call;
+
+	proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+	                                   WPAS_DBUS_SERVICE,
+	                                   op,
+	                                   WPAS_DBUS_IFACE_BSSID);
+	info = nm_supplicant_info_new (self, proxy, priv->other_pcalls);
+	call = dbus_g_proxy_begin_call (proxy, "properties",
+	                                bssid_properties_cb,
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+	g_object_unref (proxy);
+}
+
+static void
+scan_results_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	GError *err = NULL;
+	GPtrArray *array = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err,
+	                            DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &array,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_SUPPLICANT, "could not get scan results: %s.", err->message);
+		g_error_free (err);
+	} else {
+		int i;
+		NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+
+		/* Notify listeners of the result of the scan */
+		g_signal_emit (info->interface,
+		               nm_supplicant_interface_signals[SCAN_RESULTS],
+		               0,
+		               array->len);
+
+		/* Fire off a "properties" call for each returned BSSID */
+		for (i = 0; i < array->len; i++) {
+			char *op = g_ptr_array_index (array, i);
+
+			request_bssid_properties (info->interface, op);
+			g_free (op);
+		}
+
+		g_ptr_array_free (array, TRUE);
+	}
+}
+
+static gboolean
+request_scan_results (gpointer user_data)
+{
+	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	NMSupplicantInfo *info;
+	DBusGProxyCall *call;
+	GTimeVal cur_time;
+
+	priv->scan_results_timeout = 0;
+
+	g_return_val_if_fail (priv->iface_proxy != NULL, FALSE);
+
+	info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
+	call = dbus_g_proxy_begin_call (priv->iface_proxy, "scanResults",
+	                                scan_results_cb, 
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+
+	g_get_current_time (&cur_time);
+	priv->last_scan = cur_time.tv_sec;
+	return FALSE;
+}
+
+static void
+wpas_iface_query_scan_results (DBusGProxy *proxy, gpointer user_data)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data);
+	GTimeVal cur_time;
+
+	/* Only query scan results if a query is not queued */
+	if (priv->scan_results_timeout)
+		return;
+
+	g_get_current_time (&cur_time);
+
+	/* Only fetch scan results every 4s max, but initially do it right away */
+	if (priv->last_scan + 4 < cur_time.tv_sec) {
+		priv->scan_results_timeout = g_idle_add (request_scan_results,
+		                                         user_data);
+	} else {
+		priv->scan_results_timeout =
+			g_timeout_add_seconds ((4 - (cur_time.tv_sec - priv->last_scan)),
+			               request_scan_results, user_data);
+	}
+}
+
+static guint32
+wpas_state_string_to_enum (const char * str_state)
+{
+	guint32 enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED;
+
+	if (!strcmp (str_state, "DISCONNECTED"))
+		enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED;
+	else if (!strcmp (str_state, "INACTIVE"))
+		enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE;
+	else if (!strcmp (str_state, "SCANNING"))
+		enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING;
+	else if (!strcmp (str_state, "ASSOCIATING"))
+		enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING;
+	else if (!strcmp (str_state, "ASSOCIATED"))
+		enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED;
+	else if (!strcmp (str_state, "4WAY_HANDSHAKE"))
+		enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE;
+	else if (!strcmp (str_state, "GROUP_HANDSHAKE"))
+		enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE;
+	else if (!strcmp (str_state, "COMPLETED"))
+		enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED;
+
+	return enum_state;
+}
+
+static void
+wpas_iface_handle_state_change (DBusGProxy *proxy,
+                                const char *str_new_state,
+                                const char *str_old_state,
+                                gpointer user_data)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data);
+	guint32 old_state, enum_new_state;
+
+	enum_new_state = wpas_state_string_to_enum (str_new_state);
+	old_state = priv->con_state;
+	priv->con_state = enum_new_state;
+	if (priv->con_state != old_state) {
+		g_signal_emit (user_data,
+		               nm_supplicant_interface_signals[CONNECTION_STATE],
+		               0,
+		               priv->con_state,
+		               old_state);
+	}
+}
+
+
+static void
+iface_state_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	GError *err = NULL;
+	char *state_str = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err,
+	                            G_TYPE_STRING, &state_str,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_SUPPLICANT, "could not get interface state: %s.", err->message);
+		g_error_free (err);
+	} else {
+		NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+		NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+
+		priv->con_state = wpas_state_string_to_enum (state_str);
+		g_free (state_str);
+		nm_supplicant_interface_set_state (info->interface, NM_SUPPLICANT_INTERFACE_STATE_READY);
+	}
+}
+
+static void
+wpas_iface_get_state (NMSupplicantInterface *self)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	NMSupplicantInfo *info;
+	DBusGProxyCall *call;
+
+	info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
+	call = dbus_g_proxy_begin_call (priv->iface_proxy, "state",
+	                                iface_state_cb,
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+}
+
+static void
+iface_scanning_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+	gboolean scanning = FALSE;
+
+	if (dbus_g_proxy_end_call (proxy, call_id, NULL,
+	                            G_TYPE_BOOLEAN, &scanning,
+	                            G_TYPE_INVALID)) {
+		if (scanning != priv->scanning) {
+			priv->scanning = scanning;
+			g_object_notify (G_OBJECT (info->interface), "scanning");
+		}
+	}
+}
+
+static void
+wpas_iface_get_scanning (NMSupplicantInterface *self)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	NMSupplicantInfo *info;
+	DBusGProxyCall *call;
+
+	info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
+	call = dbus_g_proxy_begin_call (priv->iface_proxy, "scanning",
+	                                iface_scanning_cb,
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+}
+
+static void
+wpas_iface_handle_scanning (DBusGProxy *proxy,
+                            gboolean scanning,
+                            gpointer user_data)
+{
+	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	if (scanning != priv->scanning) {
+		priv->scanning = scanning;
+		g_object_notify (G_OBJECT (self), "scanning");
+	}
+}
+
+gboolean
+nm_supplicant_interface_get_scanning (NMSupplicantInterface *self)
+{
+	NMSupplicantInterfacePrivate *priv;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+
+	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	if (priv->scanning)
+		return TRUE;
+	if (priv->con_state == NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING)
+		return TRUE;
+	return FALSE;
+}
+
+static void
+nm_supplicant_interface_add_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+	GError *err = NULL;
+	char *path = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err,
+	                            DBUS_TYPE_G_OBJECT_PATH, &path,
+	                            G_TYPE_INVALID)) {
+
+		if (dbus_g_error_has_name (err, WPAS_ERROR_INVALID_IFACE)) {
+			/* Interface not added, try to add it */
+			nm_supplicant_interface_add_to_supplicant (info->interface, FALSE);
+		} else if (dbus_g_error_has_name (err, WPAS_ERROR_EXISTS_ERROR)) {
+			/* Interface already added, just try to get the interface */
+			nm_supplicant_interface_add_to_supplicant (info->interface, TRUE);
+		} else {
+			nm_log_err (LOGD_SUPPLICANT, "(%s): error getting interface: %s",
+			            priv->dev, err->message);
+		}
+
+		g_error_free (err);
+	} else {
+		nm_log_dbg (LOGD_SUPPLICANT, "(%s): interface added to supplicant", priv->dev);
+
+		priv->object_path = path;
+
+		priv->iface_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+		                                               WPAS_DBUS_SERVICE,
+		                                               path,
+		                                               WPAS_DBUS_IFACE_INTERFACE);
+
+		dbus_g_proxy_add_signal (priv->iface_proxy, "ScanResultsAvailable", G_TYPE_INVALID);
+
+		dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING,
+		                                   G_TYPE_NONE,
+		                                   G_TYPE_STRING, G_TYPE_STRING,
+		                                   G_TYPE_INVALID);
+
+		dbus_g_proxy_add_signal (priv->iface_proxy, "StateChange", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+
+		dbus_g_proxy_connect_signal (priv->iface_proxy, "ScanResultsAvailable",
+		                             G_CALLBACK (wpas_iface_query_scan_results),
+		                             info->interface,
+		                             NULL);
+
+		dbus_g_proxy_connect_signal (priv->iface_proxy, "StateChange",
+		                             G_CALLBACK (wpas_iface_handle_state_change),
+		                             info->interface,
+		                             NULL);
+
+		dbus_g_proxy_add_signal (priv->iface_proxy, "Scanning", G_TYPE_BOOLEAN, G_TYPE_INVALID);
+
+		dbus_g_proxy_connect_signal (priv->iface_proxy, "Scanning",
+		                             G_CALLBACK (wpas_iface_handle_scanning),
+		                             info->interface,
+		                             NULL);
+
+		/* Interface added to the supplicant; get its initial state. */
+		wpas_iface_get_state (info->interface);
+		wpas_iface_get_scanning (info->interface);
+	}
+}
+
+static void
+nm_supplicant_interface_add_to_supplicant (NMSupplicantInterface * self,
+                                           gboolean get_only)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	NMSupplicantInfo *info;
+	DBusGProxy *proxy;
+	DBusGProxyCall *call;
+
+	proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+	                                   WPAS_DBUS_SERVICE,
+	                                   WPAS_DBUS_PATH,
+	                                   WPAS_DBUS_INTERFACE);
+	info = nm_supplicant_info_new (self, proxy, priv->other_pcalls);
+
+	if (get_only) {
+		call = dbus_g_proxy_begin_call (proxy, "getInterface",
+		                                nm_supplicant_interface_add_cb,
+		                                info,
+		                                nm_supplicant_info_destroy,
+		                                G_TYPE_STRING, priv->dev,
+		                                G_TYPE_INVALID);
+	} else {
+		GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal);
+		GValue *driver;
+
+		driver = g_new0 (GValue, 1);
+		g_value_init (driver, G_TYPE_STRING);
+		g_value_set_string (driver, priv->is_wireless ? "wext" : "wired");
+		g_hash_table_insert (hash, "driver", driver);
+
+		call = dbus_g_proxy_begin_call (proxy, "addInterface",
+		                                nm_supplicant_interface_add_cb,
+		                                info,
+		                                nm_supplicant_info_destroy,
+		                                G_TYPE_STRING, priv->dev,
+		                                DBUS_TYPE_G_MAP_OF_VARIANT, hash,
+		                                G_TYPE_INVALID);
+
+		g_value_unset (driver);
+		g_free (driver);
+		g_hash_table_destroy (hash);
+	}
+
+	g_object_unref (proxy);
+
+	nm_supplicant_info_set_call (info, call);
+}
+
+static void
+nm_supplicant_interface_start (NMSupplicantInterface * self)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	guint32          state;
+
+	/* Can only start the interface from INIT state */
+	g_return_if_fail (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT);
+
+	nm_log_dbg (LOGD_SUPPLICANT, "(%s): adding interface to supplicant", priv->dev);
+
+	state = nm_supplicant_manager_get_state (priv->smgr);
+	if (state == NM_SUPPLICANT_MANAGER_STATE_IDLE) {
+		nm_supplicant_interface_set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING);
+		nm_supplicant_interface_add_to_supplicant (self, FALSE);
+	} else if (state == NM_SUPPLICANT_MANAGER_STATE_DOWN) {
+		/* Don't do anything; wait for signal from supplicant manager
+		 * that its state has changed.
+		 */
+	} else
+		nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant manager state!");
+}
+
+static void
+nm_supplicant_interface_handle_supplicant_manager_idle_state (NMSupplicantInterface * self)
+{
+	switch (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state) {
+		case NM_SUPPLICANT_INTERFACE_STATE_INIT:
+			/* Move to STARTING state when supplicant is ready */
+			nm_supplicant_interface_start (self);
+			break;
+		case NM_SUPPLICANT_INTERFACE_STATE_STARTING:
+			/* Don't do anything here, though we should never hit this */
+			break;
+		case NM_SUPPLICANT_INTERFACE_STATE_READY:
+			/* Don't do anything here, though we should never hit this */
+			break;
+		case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
+			/* Don't do anything here; interface can't get out of DOWN state */
+			break;
+		default:
+			nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant interface state!");
+			break;
+	}
+}
+
+
+static void
+nm_supplicant_interface_set_state (NMSupplicantInterface * self,
+                                   guint32 new_state)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	guint32 old_state;
+
+	g_return_if_fail (new_state < NM_SUPPLICANT_INTERFACE_STATE_LAST);
+
+	if (new_state == priv->state)
+		return;
+
+	old_state = priv->state;
+	if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
+		/* If the interface is transitioning to DOWN and there's are
+		 * in-progress pending calls, cancel them.
+		 */
+		cancel_all_callbacks (priv->other_pcalls);
+		cancel_all_callbacks (priv->assoc_pcalls);
+	}
+
+	priv->state = new_state;
+	g_signal_emit (self,
+	               nm_supplicant_interface_signals[STATE],
+	               0,
+	               priv->state,
+	               old_state);
+}
+
+static void
+nm_supplicant_interface_smgr_state_changed (NMSupplicantManager * smgr,
+                                            guint32 new_state,
+                                            guint32 old_state,
+                                            gpointer user_data)
+{
+	NMSupplicantInterface * self = NM_SUPPLICANT_INTERFACE (user_data);
+
+	switch (new_state) {
+		case NM_SUPPLICANT_MANAGER_STATE_DOWN:
+			/* The supplicant went away, likely the connection to it is also
+			 * gone.  Therefore, this interface must move to the DOWN state
+			 * and be disposed of.
+			 */
+			nm_supplicant_interface_set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
+			break;
+		case NM_SUPPLICANT_MANAGER_STATE_IDLE:
+			/* Handle the supplicant now being available. */
+			nm_supplicant_interface_handle_supplicant_manager_idle_state (self);
+			break;
+		default:
+			nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant manager state!");
+			break;
+	}
+}
+
+
+static void
+remove_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	GError *err = NULL;
+	guint tmp;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+		nm_log_dbg (LOGD_SUPPLICANT, "Couldn't remove network from supplicant interface: %s.",
+		            err->message);
+		g_error_free (err);
+	}
+}
+
+static void
+disconnect_cb  (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	GError *err = NULL;
+	guint tmp;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't disconnect supplicant interface: %s.",
+		             err->message);
+		g_error_free (err);
+	}
+}
+
+void
+nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
+{
+	NMSupplicantInterfacePrivate *priv;
+
+	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self));
+
+	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	/* Clear and cancel all pending calls related to a prior
+	 * connection attempt.
+	 */
+	cancel_all_callbacks (priv->assoc_pcalls);
+
+	/* Don't do anything if there is no connection to the supplicant yet. */
+	if (!priv->iface_proxy)
+		return;
+
+	/* Don't try to disconnect if the supplicant interface is already
+	 * disconnected.
+	 */
+	if (priv->con_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED
+	    || priv->con_state == NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE) {
+		if (priv->net_proxy) {
+			g_object_unref (priv->net_proxy);
+			priv->net_proxy = NULL;
+		}
+
+		return;
+	}
+
+	/* Remove any network that was added by NetworkManager */
+	if (priv->net_proxy) {
+		dbus_g_proxy_begin_call (priv->iface_proxy, "removeNetwork",
+		                         remove_network_cb,
+		                         NULL, NULL,
+		                         DBUS_TYPE_G_OBJECT_PATH, dbus_g_proxy_get_path (priv->net_proxy),
+		                         G_TYPE_INVALID);
+
+		g_object_unref (priv->net_proxy);
+		priv->net_proxy = NULL;
+	}
+
+	dbus_g_proxy_begin_call (priv->iface_proxy, "disconnect",
+	                         disconnect_cb,
+	                         NULL, NULL,
+	                         G_TYPE_INVALID);
+}
+
+static void
+select_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	GError *err = NULL;
+	guint tmp;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't select network config: %s.", err->message);
+		emit_error_helper (info->interface, err);
+		g_error_free (err);
+	}
+}
+
+static void
+set_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+	GError *err = NULL;
+	guint tmp;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't set network config: %s.", err->message);
+		emit_error_helper (info->interface, err);
+		g_error_free (err);
+	} else {
+		DBusGProxyCall *call;
+
+		info = nm_supplicant_info_new (info->interface, priv->iface_proxy, priv->assoc_pcalls);
+		call = dbus_g_proxy_begin_call (priv->iface_proxy, "selectNetwork",
+		                                select_network_cb,
+		                                info,
+		                                nm_supplicant_info_destroy,
+		                                DBUS_TYPE_G_OBJECT_PATH, dbus_g_proxy_get_path (proxy),
+		                                G_TYPE_INVALID);
+		nm_supplicant_info_set_call (info, call);
+	}
+}
+
+static void
+call_set_network (NMSupplicantInfo *info)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+	GHashTable *config_hash;
+	DBusGProxyCall *call;
+
+	config_hash = nm_supplicant_config_get_hash (priv->cfg);
+	call = dbus_g_proxy_begin_call (priv->net_proxy, "set",
+	                                set_network_cb,
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                DBUS_TYPE_G_MAP_OF_VARIANT, config_hash,
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+	g_hash_table_destroy (config_hash);
+}
+
+static void
+set_blobs_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+	GError *err = NULL;
+	guint tmp;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't set network certificates: %s.", err->message);
+		emit_error_helper (info->interface, err);
+		g_error_free (err);
+	} else {
+		info = nm_supplicant_info_new (info->interface, priv->iface_proxy, priv->assoc_pcalls);
+		call_set_network (info);
+	}
+}
+
+static GValue *
+byte_array_to_gvalue (const GByteArray *array)
+{
+	GValue *val;
+
+	val = g_slice_new0 (GValue);
+	g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY);
+	g_value_set_boxed (val, array);
+
+	return val;
+}
+
+static void
+blob_free (GValue *val)
+{
+	g_value_unset (val);
+	g_slice_free (GValue, val);
+}
+
+static void
+convert_blob (const char *key, const GByteArray *value, GHashTable *hash)
+{
+	GValue *val;
+
+	val = byte_array_to_gvalue (value);
+	g_hash_table_insert (hash, g_strdup (key), val);
+}
+
+static void
+call_set_blobs (NMSupplicantInfo *info, GHashTable *orig_blobs)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+	DBusGProxyCall *call;
+	GHashTable *blobs;
+
+	blobs = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                               (GDestroyNotify) g_free,
+	                               (GDestroyNotify) blob_free);
+	if (!blobs) {
+		const char *msg = "Not enough memory to create blob table.";
+
+		nm_log_warn (LOGD_SUPPLICANT, "%s", msg);
+		g_signal_emit (info->interface,
+		               nm_supplicant_interface_signals[CONNECTION_ERROR],
+		               0, "SendBlobError", msg);
+		return;
+	}
+
+	g_hash_table_foreach (orig_blobs, (GHFunc) convert_blob, blobs);
+
+	call = dbus_g_proxy_begin_call (priv->iface_proxy, "setBlobs",
+	                                set_blobs_cb,
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                DBUS_TYPE_G_MAP_OF_VARIANT, blobs,
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+	g_hash_table_destroy (blobs);
+}
+
+static void
+add_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	GError *err = NULL;
+	char *path = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err,
+	                            DBUS_TYPE_G_OBJECT_PATH, &path,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't add a network to the supplicant interface: %s.",
+		             err->message);
+		emit_error_helper (info->interface, err);
+		g_error_free (err);
+	} else {
+		NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+		GHashTable *blobs;
+
+		priv->net_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+		                                             WPAS_DBUS_SERVICE,
+		                                             path,
+		                                             WPAS_DBUS_IFACE_NETWORK);
+		g_free (path);
+
+		info = nm_supplicant_info_new (info->interface,
+		                               priv->net_proxy,
+		                               priv->assoc_pcalls);
+		/* Send any blobs first; if there aren't any jump to sending the
+		 * config settings.
+		 */
+		blobs = nm_supplicant_config_get_blobs (priv->cfg);
+		if (g_hash_table_size (blobs) > 0)
+			call_set_blobs (info, blobs);
+		else
+			call_set_network (info);
+	}
+}
+
+static void
+set_ap_scan_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
+	GError *err = NULL;
+	guint32 tmp;
+	DBusGProxyCall *call;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_SUPPLICANT, "Couldn't send AP scan mode to the supplicant interface: %s.",
+		             err->message);
+		emit_error_helper (info->interface, err);
+		g_error_free (err);
+		return;
+	}
+
+	nm_log_info (LOGD_SUPPLICANT, "Config: set interface ap_scan to %d",
+	             nm_supplicant_config_get_ap_scan (priv->cfg));
+
+	info = nm_supplicant_info_new (info->interface, proxy, info->store);
+	call = dbus_g_proxy_begin_call (proxy, "addNetwork",
+	                                add_network_cb,
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+}
+
+gboolean
+nm_supplicant_interface_set_config (NMSupplicantInterface * self,
+                                    NMSupplicantConfig * cfg)
+{
+	NMSupplicantInterfacePrivate *priv;
+	NMSupplicantInfo *info;
+	DBusGProxyCall *call;
+	guint32 ap_scan;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
+
+	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	nm_supplicant_interface_disconnect (self);
+	
+	if (priv->cfg)
+		g_object_unref (priv->cfg);
+	priv->cfg = cfg;
+
+	if (cfg == NULL)
+		return TRUE;
+
+	g_object_ref (priv->cfg);
+
+	info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
+	ap_scan = nm_supplicant_config_get_ap_scan (priv->cfg);
+	call = dbus_g_proxy_begin_call (priv->iface_proxy, "setAPScan",
+	                                set_ap_scan_cb,
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                G_TYPE_UINT, ap_scan,
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+
+	return call != NULL;
+}
+
+const char *
+nm_supplicant_interface_get_device (NMSupplicantInterface * self)
+{
+	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL);
+
+	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev;
+}
+
+static void
+scan_request_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+{
+	NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+	GError *err = NULL;
+	guint32 success = 0;
+
+	if (!dbus_g_proxy_end_call (proxy, call_id, &err,
+	                            G_TYPE_UINT, &success,
+	                            G_TYPE_INVALID)) {
+		nm_log_warn (LOGD_SUPPLICANT, "Could not get scan request result: %s", err->message);
+		g_error_free (err);
+	} 
+
+	/* Notify listeners of the result of the scan */
+	g_signal_emit (info->interface,
+	               nm_supplicant_interface_signals[SCAN_REQ_RESULT],
+	               0,
+	               success ? TRUE : FALSE);
+}
+
+gboolean
+nm_supplicant_interface_request_scan (NMSupplicantInterface * self)
+{
+	NMSupplicantInterfacePrivate *priv;
+	NMSupplicantInfo *info;
+	DBusGProxyCall *call;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
+
+	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
+	call = dbus_g_proxy_begin_call (priv->iface_proxy, "scan",
+	                                scan_request_cb,
+	                                info,
+	                                nm_supplicant_info_destroy,
+	                                G_TYPE_INVALID);
+	nm_supplicant_info_set_call (info, call);
+
+	return call != NULL;
+}
+
+guint32
+nm_supplicant_interface_get_state (NMSupplicantInterface * self)
+{
+	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NM_SUPPLICANT_INTERFACE_STATE_DOWN);
+
+	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state;
+}
+
+guint32
+nm_supplicant_interface_get_connection_state (NMSupplicantInterface * self)
+{
+	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED);
+
+	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->con_state;
+}
+
+const char *
+nm_supplicant_interface_state_to_string (guint32 state)
+{
+	switch (state) {
+	case NM_SUPPLICANT_INTERFACE_STATE_INIT:
+		return "init";
+	case NM_SUPPLICANT_INTERFACE_STATE_STARTING:
+		return "starting";
+	case NM_SUPPLICANT_INTERFACE_STATE_READY:
+		return "ready";
+	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
+		return "down";
+	default:
+		break;
+	}
+	return "unknown";
+}
+
+const char *
+nm_supplicant_interface_connection_state_to_string (guint32 state)
+{
+	switch (state) {
+	case NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED:
+		return "disconnected";
+	case NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE:
+		return "inactive";
+	case NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING:
+		return "scanning";
+	case NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING:
+		return "associating";
+	case NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED:
+		return "associated";
+	case NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE:
+		return "4-way handshake";
+	case NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE:
+		return "group handshake";
+	case NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED:
+		return "completed";
+	default:
+		break;
+	}
+	return "unknown";
+}
+
diff --git a/src/supplicant-manager/nm-supplicant-interface.h b/src/supplicant-manager/nm-supplicant-interface.h
new file mode 100644
index 00000000..bee5436f
--- /dev/null
+++ b/src/supplicant-manager/nm-supplicant-interface.h
@@ -0,0 +1,145 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SUPPLICANT_INTERFACE_H
+#define NM_SUPPLICANT_INTERFACE_H
+
+#include <glib-object.h>
+#include <dbus/dbus.h>
+#include "nm-supplicant-types.h"
+
+G_BEGIN_DECLS
+
+/*
+ * Supplicant interface states
+ *   The states are linear, ie INIT -> READY -> DOWN and state may only be
+ *   changed in one direction.  If an interface reaches the DOWN state, it
+ *   cannot be re-initialized; it must be torn down and a new one created.
+ *
+ * INIT:  interface has been created, but cannot be used yet; it is waiting
+ *             for pending requests of the supplicant to complete.
+ * READY: interface is ready for use
+ * DOWN:  interface has been removed or has otherwise been made invalid; it
+ *             must be torn down.
+ *
+ * Note: LAST is an invalid state and only used for boundary checking.
+ */
+enum {
+	NM_SUPPLICANT_INTERFACE_STATE_INIT = 0,
+	NM_SUPPLICANT_INTERFACE_STATE_STARTING,
+	NM_SUPPLICANT_INTERFACE_STATE_READY,
+	NM_SUPPLICANT_INTERFACE_STATE_DOWN,
+	NM_SUPPLICANT_INTERFACE_STATE_LAST
+};
+
+
+/*
+ * Supplicant interface connection states
+ *   The wpa_supplicant state for the connection.
+ */
+enum {
+	NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED = 0,
+	NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE,
+	NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING,
+	NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING,
+	NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED,
+	NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE,
+	NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE,
+	NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED,
+	NM_SUPPLICANT_INTERFACE_CON_STATE_LAST
+};
+
+#define NM_TYPE_SUPPLICANT_INTERFACE            (nm_supplicant_interface_get_type ())
+#define NM_SUPPLICANT_INTERFACE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterface))
+#define NM_SUPPLICANT_INTERFACE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterfaceClass))
+#define NM_IS_SUPPLICANT_INTERFACE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SUPPLICANT_INTERFACE))
+#define NM_IS_SUPPLICANT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_SUPPLICANT_INTERFACE))
+#define NM_SUPPLICANT_INTERFACE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterfaceClass))
+
+struct _NMSupplicantInterface {
+	GObject parent;
+};
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+
+	/* change in the interface's state */
+	void (*state)            (NMSupplicantInterface * iface,
+	                          guint32 new_state,
+	                          guint32 old_state);
+
+	/* interface was removed by the supplicant */
+	void (*removed)          (NMSupplicantInterface * iface);
+
+	/* interface saw a new access point from a scan */
+	void (*scanned_ap)       (NMSupplicantInterface * iface,
+	                          DBusMessage * message);
+
+	/* result of a wireless scan request */
+	void (*scan_req_result)  (NMSupplicantInterface * iface,
+	                          gboolean success);
+
+	/* scan results returned from supplicant */
+	void (*scan_results)     (NMSupplicantInterface * iface,
+	                          guint num_bssids);
+
+	/* link state of the device's connection */
+	void (*connection_state) (NMSupplicantInterface * iface,
+	                          guint32 new_state,
+	                          guint32 old_state);
+
+	/* an error occurred during a connection request */
+	void (*connection_error) (NMSupplicantInterface * iface,
+	                          const char * name,
+	                          const char * message);
+} NMSupplicantInterfaceClass;
+
+
+GType nm_supplicant_interface_get_type (void);
+
+NMSupplicantInterface * nm_supplicant_interface_new (NMSupplicantManager * smgr,
+                                                     const char *ifname,
+                                                     gboolean is_wireless);
+
+gboolean nm_supplicant_interface_set_config (NMSupplicantInterface * iface,
+                                             NMSupplicantConfig * cfg);
+
+void nm_supplicant_interface_disconnect (NMSupplicantInterface * iface);
+
+const char * nm_supplicant_interface_get_device (NMSupplicantInterface * iface);
+
+gboolean nm_supplicant_interface_request_scan (NMSupplicantInterface * self);
+
+guint32 nm_supplicant_interface_get_state (NMSupplicantInterface * self);
+
+guint32 nm_supplicant_interface_get_connection_state (NMSupplicantInterface * self);
+
+const char *nm_supplicant_interface_state_to_string (guint32 state);
+
+const char *nm_supplicant_interface_connection_state_to_string (guint32 state);
+
+gboolean nm_supplicant_interface_get_scanning (NMSupplicantInterface *self);
+
+G_END_DECLS
+
+#endif	/* NM_SUPPLICANT_INTERFACE_H */
diff --git a/src/supplicant-manager/nm-supplicant-manager.c b/src/supplicant-manager/nm-supplicant-manager.c
new file mode 100644
index 00000000..a2cf58eb
--- /dev/null
+++ b/src/supplicant-manager/nm-supplicant-manager.c
@@ -0,0 +1,343 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "nm-supplicant-manager.h"
+#include "nm-supplicant-interface.h"
+#include "nm-dbus-manager.h"
+#include "nm-marshal.h"
+#include "nm-logging.h"
+#include "nm-glib-compat.h"
+
+#define SUPPLICANT_POKE_INTERVAL 120
+
+typedef struct {
+	NMDBusManager *	dbus_mgr;
+	guint32         state;
+	GSList *        ifaces;
+	gboolean        dispose_has_run;
+	guint			poke_id;
+} NMSupplicantManagerPrivate;
+
+#define NM_SUPPLICANT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                              NM_TYPE_SUPPLICANT_MANAGER, \
+                                              NMSupplicantManagerPrivate))
+
+G_DEFINE_TYPE (NMSupplicantManager, nm_supplicant_manager, G_TYPE_OBJECT)
+
+
+static void nm_supplicant_manager_name_owner_changed (NMDBusManager *dbus_mgr,
+                                                      const char *name,
+                                                      const char *old,
+                                                      const char *new,
+                                                      gpointer user_data);
+
+static void nm_supplicant_manager_set_state (NMSupplicantManager * self,
+                                             guint32 new_state);
+
+static gboolean nm_supplicant_manager_startup (NMSupplicantManager * self);
+
+
+/* Signals */
+enum {
+	STATE,       /* change in the manager's state */
+	LAST_SIGNAL
+};
+static guint nm_supplicant_manager_signals[LAST_SIGNAL] = { 0 };
+
+
+NMSupplicantManager *
+nm_supplicant_manager_get (void)
+{
+	static NMSupplicantManager * singleton = NULL;
+
+	if (!singleton) {
+		singleton = NM_SUPPLICANT_MANAGER (g_object_new (NM_TYPE_SUPPLICANT_MANAGER, NULL));
+	} else {
+		g_object_ref (singleton);
+	}
+
+	g_assert (singleton);
+	return singleton;
+}
+
+static gboolean
+poke_supplicant_cb (gpointer user_data)
+{
+	NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	DBusGConnection *g_connection;
+	DBusGProxy *proxy;
+	const char *tmp = "ignoreme";
+
+	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	proxy = dbus_g_proxy_new_for_name (g_connection,
+	                                   WPAS_DBUS_SERVICE,
+	                                   WPAS_DBUS_PATH,
+	                                   WPAS_DBUS_INTERFACE);
+	if (!proxy) {
+		nm_log_warn (LOGD_SUPPLICANT, "Error: could not init wpa_supplicant proxy");
+		goto out;
+	}
+
+	nm_log_info (LOGD_SUPPLICANT, "Trying to start the supplicant...");
+	dbus_g_proxy_call_no_reply (proxy, "getInterface", G_TYPE_STRING, tmp, G_TYPE_INVALID);
+	g_object_unref (proxy);
+
+out:
+	/* Reschedule the poke */	
+	priv->poke_id = g_timeout_add_seconds (SUPPLICANT_POKE_INTERVAL,
+	                               poke_supplicant_cb,
+	                               (gpointer) self);
+
+	return FALSE;
+}
+
+static void
+nm_supplicant_manager_init (NMSupplicantManager * self)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	gboolean running;
+
+	priv->dispose_has_run = FALSE;
+	priv->state = NM_SUPPLICANT_MANAGER_STATE_DOWN;
+	priv->dbus_mgr = nm_dbus_manager_get ();
+	priv->poke_id = 0;
+
+	running = nm_supplicant_manager_startup (self);
+
+	g_signal_connect (priv->dbus_mgr,
+	                  "name-owner-changed",
+	                  G_CALLBACK (nm_supplicant_manager_name_owner_changed),
+	                  self);
+
+	if (!running) {
+		/* Try to activate the supplicant */
+		priv->poke_id = g_idle_add (poke_supplicant_cb, (gpointer) self);
+	}
+}
+
+static void
+nm_supplicant_manager_dispose (GObject *object)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (object);
+
+	if (priv->dispose_has_run) {
+		G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
+		return;
+	}
+
+	priv->dispose_has_run = TRUE;
+
+	if (priv->poke_id) {
+		g_source_remove (priv->poke_id);
+		priv->poke_id = 0;
+	}
+
+	if (priv->dbus_mgr) {
+		g_object_unref (G_OBJECT (priv->dbus_mgr));
+		priv->dbus_mgr = NULL;
+	}
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
+}
+
+static void
+nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass)
+{
+	GObjectClass * object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMSupplicantManagerPrivate));
+
+	object_class->dispose = nm_supplicant_manager_dispose;
+
+	/* Signals */
+	nm_supplicant_manager_signals[STATE] =
+		g_signal_new ("state",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMSupplicantManagerClass, state),
+		              NULL, NULL,
+		              _nm_marshal_VOID__UINT_UINT,
+		              G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+}
+
+static void
+nm_supplicant_manager_name_owner_changed (NMDBusManager *dbus_mgr,
+                                          const char *name,
+										  const char *old_owner,
+										  const char *new_owner,
+                                          gpointer user_data)
+{
+	NMSupplicantManager * self = (NMSupplicantManager *) user_data;
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	gboolean old_owner_good = (old_owner && strlen (old_owner));
+	gboolean new_owner_good = (new_owner && strlen (new_owner));
+
+	/* Can't handle the signal if its not from the supplicant service */
+	if (strcmp (WPAS_DBUS_SERVICE, name) != 0)
+		return;
+
+	if (!old_owner_good && new_owner_good) {
+		gboolean running;
+
+		running = nm_supplicant_manager_startup (self);
+
+		if (running && priv->poke_id) {
+			g_source_remove (priv->poke_id);
+			priv->poke_id = 0;
+		}
+	} else if (old_owner_good && !new_owner_good) {
+		nm_supplicant_manager_set_state (self, NM_SUPPLICANT_MANAGER_STATE_DOWN);
+
+		if (priv->poke_id)
+			g_source_remove (priv->poke_id);
+
+		/* Poke the supplicant so that it gets activated by dbus system bus
+		 * activation.
+		 */
+		priv->poke_id = g_idle_add (poke_supplicant_cb, (gpointer) self);
+	}
+}
+
+
+guint32
+nm_supplicant_manager_get_state (NMSupplicantManager * self)
+{
+	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), FALSE);
+
+	return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->state;
+}
+
+static void
+nm_supplicant_manager_set_state (NMSupplicantManager * self, guint32 new_state)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	guint32 old_state;
+
+	if (new_state == priv->state)
+		return;
+
+	old_state = priv->state;
+	priv->state = new_state;
+	g_signal_emit (self,
+	               nm_supplicant_manager_signals[STATE],
+	               0,
+	               priv->state,
+	               old_state);
+}
+
+static gboolean
+nm_supplicant_manager_startup (NMSupplicantManager * self)
+{
+	gboolean running;
+
+	/* FIXME: convert to pending call */
+	running = nm_dbus_manager_name_has_owner (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->dbus_mgr,
+	                                          WPAS_DBUS_SERVICE);
+	if (running)
+		nm_supplicant_manager_set_state (self, NM_SUPPLICANT_MANAGER_STATE_IDLE);
+
+	return running;
+}
+
+NMSupplicantInterface *
+nm_supplicant_manager_get_iface (NMSupplicantManager * self,
+								 const char *ifname,
+								 gboolean is_wireless)
+{
+	NMSupplicantManagerPrivate *priv;
+	NMSupplicantInterface * iface = NULL;
+	GSList * elt;
+
+	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
+	g_return_val_if_fail (ifname != NULL, NULL);
+
+	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+
+	/* Ensure we don't already have this interface */
+	for (elt = priv->ifaces; elt; elt = g_slist_next (elt)) {
+		NMSupplicantInterface * if_tmp = (NMSupplicantInterface *) elt->data;
+
+		if (!strcmp (ifname, nm_supplicant_interface_get_device (if_tmp))) {
+			iface = if_tmp;
+			break;
+		}
+	}
+
+	if (!iface) {
+		nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname);
+		iface = nm_supplicant_interface_new (self, ifname, is_wireless);
+		if (iface)
+			priv->ifaces = g_slist_append (priv->ifaces, iface);
+	} else {
+		nm_log_dbg (LOGD_SUPPLICANT, "(%s): returning existing supplicant interface", ifname);
+	}
+
+	return iface;
+}
+
+void
+nm_supplicant_manager_release_iface (NMSupplicantManager * self,
+                                     NMSupplicantInterface * iface)
+{
+	NMSupplicantManagerPrivate *priv;
+	GSList * elt;
+
+	g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
+	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (iface));
+
+	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+
+	for (elt = priv->ifaces; elt; elt = g_slist_next (elt)) {
+		NMSupplicantInterface * if_tmp = (NMSupplicantInterface *) elt->data;
+
+		if (if_tmp == iface) {
+			/* Remove the iface from the supplicant manager's list and
+			 * dereference to match additional reference in get_iface.
+			 */
+			priv->ifaces = g_slist_remove_link (priv->ifaces, elt);
+			g_slist_free_1 (elt);
+			g_object_unref (iface);
+			break;
+		}
+	}
+}
+
+const char *
+nm_supplicant_manager_state_to_string (guint32 state)
+{
+	switch (state) {
+	case NM_SUPPLICANT_MANAGER_STATE_DOWN:
+		return "down";
+	case NM_SUPPLICANT_MANAGER_STATE_IDLE:
+		return "idle";
+	default:
+		break;
+	}
+	return "unknown";
+}
+
+
diff --git a/src/supplicant-manager/nm-supplicant-manager.h b/src/supplicant-manager/nm-supplicant-manager.h
new file mode 100644
index 00000000..fef2a774
--- /dev/null
+++ b/src/supplicant-manager/nm-supplicant-manager.h
@@ -0,0 +1,88 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SUPPLICANT_MANAGER_H
+#define NM_SUPPLICANT_MANAGER_H
+
+#include <glib-object.h>
+#include "nm-supplicant-types.h"
+#include "nm-device.h"
+
+#define WPAS_DBUS_SERVICE	"fi.epitest.hostap.WPASupplicant"
+#define WPAS_DBUS_PATH		"/fi/epitest/hostap/WPASupplicant"
+#define WPAS_DBUS_INTERFACE	"fi.epitest.hostap.WPASupplicant"
+
+
+G_BEGIN_DECLS
+
+/*
+ * Supplicant manager states
+ *   Either state may transition to the other state at any time.
+ *
+ * DOWN: supplicant manager has been created, but cannot be used; the supplicant
+ *           is either not running or has not yet been fully initialized.
+ * IDLE: supplicant manager is ready for use
+ *
+ * Note: LAST is an invalid state and only used for boundary checking.
+ */
+enum {
+	NM_SUPPLICANT_MANAGER_STATE_DOWN = 0,
+	NM_SUPPLICANT_MANAGER_STATE_IDLE,
+	NM_SUPPLICANT_MANAGER_STATE_LAST
+};
+
+
+#define NM_TYPE_SUPPLICANT_MANAGER				(nm_supplicant_manager_get_type ())
+#define NM_SUPPLICANT_MANAGER(obj)				(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManager))
+#define NM_SUPPLICANT_MANAGER_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass))
+#define NM_IS_SUPPLICANT_MANAGER(obj)			(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SUPPLICANT_MANAGER))
+#define NM_IS_SUPPLICANT_MANAGER_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_SUPPLICANT_MANAGER))
+#define NM_SUPPLICANT_MANAGER_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass))
+
+struct _NMSupplicantManager
+{
+	GObject parent;
+};
+
+typedef struct
+{
+	GObjectClass parent;
+
+	/* class members */
+	void (* state)  (NMSupplicantManager * mgr, guint32 new_state, guint32 old_state);
+} NMSupplicantManagerClass;
+
+GType nm_supplicant_manager_get_type (void);
+
+NMSupplicantManager * nm_supplicant_manager_get (void);
+
+guint32 nm_supplicant_manager_get_state (NMSupplicantManager * mgr);
+
+NMSupplicantInterface * nm_supplicant_manager_get_iface (NMSupplicantManager * mgr,
+                                                         const char *ifname,
+														 gboolean is_wireless);
+
+void nm_supplicant_manager_release_iface (NMSupplicantManager * mgr,
+                                          NMSupplicantInterface * iface);
+
+const char *nm_supplicant_manager_state_to_string (guint32 state);
+
+#endif /* NM_SUPPLICANT_MANAGER_H */
diff --git a/src/supplicant-manager/nm-supplicant-settings-verify.c b/src/supplicant-manager/nm-supplicant-settings-verify.c
new file mode 100644
index 00000000..28334655
--- /dev/null
+++ b/src/supplicant-manager/nm-supplicant-settings-verify.c
@@ -0,0 +1,238 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "nm-supplicant-settings-verify.h"
+
+struct Opt {
+	const char *     key;
+	const OptType    type;
+	const gint32     int_low;  /* Inclusive */
+	const gint32     int_high; /* Inclusive; max length for strings */
+	const gboolean   str_allowed_multiple;
+	const char **    str_allowed;
+};
+
+
+static gboolean validate_type_int     (const struct Opt * opt,
+                                       const char * value,
+                                       const guint32 len);
+
+static gboolean validate_type_bytes   (const struct Opt * opt,
+                                       const char * value,
+                                       const guint32 len);
+
+static gboolean validate_type_keyword (const struct Opt * opt,
+                                       const char * value,
+                                       const guint32 len);
+
+typedef gboolean (*validate_func)(const struct Opt *, const char *, const guint32);
+
+struct validate_entry {
+	const OptType  type;
+	const validate_func func;
+};
+
+static const struct validate_entry validate_table[] = {
+	{ TYPE_INT,     validate_type_int     },
+	{ TYPE_BYTES,   validate_type_bytes   },
+	{ TYPE_KEYWORD, validate_type_keyword },
+};
+
+
+const char * pairwise_allowed[] = { "CCMP", "TKIP", "NONE", NULL };
+const char * group_allowed[] =    { "CCMP", "TKIP", "WEP104", "WEP40", NULL };
+const char * proto_allowed[] =    { "WPA", "RSN", NULL };
+const char * key_mgmt_allowed[] = { "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE",
+                                    "NONE", NULL };
+const char * auth_alg_allowed[] = { "OPEN", "SHARED", "LEAP", NULL };
+const char * eap_allowed[] =      { "LEAP", "MD5", "TLS", "PEAP", "TTLS", "SIM",
+                                    "PSK", "FAST", NULL };
+
+const char * phase1_allowed[] =   {"peapver=0", "peapver=1", "peaplabel=1",
+                                    "peap_outer_success=0", "include_tls_length=1",
+                                    "sim_min_num_chal=3", NULL };
+const char * phase2_allowed[] =   {"auth=PAP", "auth=CHAP", "auth=MSCHAP",
+                                   "auth=MSCHAPV2", "auth=GTC", "auth=OTP",
+                                   "auth=MD5", "auth=TLS", "autheap=MD5",
+                                   "autheap=MSCHAPV2", "autheap=OTP",
+                                   "autheap=GTC", "autheap=TLS", NULL };
+
+static const struct Opt opt_table[] = {
+	{ "ssid",               TYPE_BYTES,   0, 32,FALSE,  NULL },
+	{ "bssid",              TYPE_KEYWORD, 0, 0, FALSE,  NULL },
+	{ "scan_ssid",          TYPE_INT,     0, 1, FALSE,  NULL },
+	{ "mode",               TYPE_INT,     0, 1, FALSE,  NULL },
+	{ "frequency",          TYPE_INT,     2412, 5825, FALSE,  NULL },
+	{ "auth_alg",           TYPE_KEYWORD, 0, 0, FALSE,  auth_alg_allowed },
+	{ "psk",                TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "pairwise",           TYPE_KEYWORD, 0, 0, FALSE,  pairwise_allowed },
+	{ "group",              TYPE_KEYWORD, 0, 0, FALSE,  group_allowed },
+	{ "proto",              TYPE_KEYWORD, 0, 0, FALSE,  proto_allowed },
+	{ "key_mgmt",           TYPE_KEYWORD, 0, 0, FALSE,  key_mgmt_allowed },
+	{ "wep_key0",           TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "wep_key1",           TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "wep_key2",           TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "wep_key3",           TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "wep_tx_keyidx",      TYPE_INT,     0, 3, FALSE,  NULL },
+	{ "eapol_flags",        TYPE_INT,     0, 3, FALSE,  NULL },
+	{ "eap",                TYPE_KEYWORD, 0, 0, FALSE,  eap_allowed },
+	{ "identity",           TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "password",           TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "ca_path",            TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "ca_cert",            TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "client_cert",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "private_key",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "private_key_passwd", TYPE_BYTES,   0, 1024, FALSE,  NULL },
+	{ "phase1",             TYPE_KEYWORD, 0, 0, TRUE, phase1_allowed },
+	{ "phase2",             TYPE_KEYWORD, 0, 0, TRUE, phase2_allowed },
+	{ "anonymous_identity", TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "ca_path2",           TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "ca_cert2",           TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "client_cert2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "private_key2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "private_key2_passwd",TYPE_BYTES,   0, 1024, FALSE,  NULL },
+	{ "pin",                TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "pcsc",               TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "nai",                TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "eappsk",             TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "pac_file",           TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "engine",             TYPE_INT,     0, 1, FALSE,  NULL },
+	{ "engine_id",          TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "key_id",             TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "fragment_size",      TYPE_INT,     1, 2000, FALSE,  NULL },
+};
+
+
+static gboolean
+validate_type_int (const struct Opt * opt,
+                   const char * value,
+                   const guint32 len)
+{
+	long int intval;
+
+	g_return_val_if_fail (opt != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+
+	errno = 0;
+	intval = strtol (value, NULL, 10);
+	if (errno != 0)
+		return FALSE;
+
+	/* strtol returns a long, but we are dealing with ints */
+	if (intval > INT_MAX || intval < INT_MIN)
+		return FALSE;
+	if (intval > opt->int_high || intval < opt->int_low)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+validate_type_bytes (const struct Opt * opt,
+                     const char * value,
+                     const guint32 len)
+{
+	guint32 check_len;
+
+	g_return_val_if_fail (opt != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+
+	check_len = opt->int_high ? opt->int_high : 255;
+	if (len > check_len)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+validate_type_keyword (const struct Opt * opt,
+                       const char * value,
+                       const guint32 len)
+{
+	char **		allowed;
+	gchar **	candidates = NULL;
+	char **		candidate;
+	gboolean	found = FALSE;
+
+	g_return_val_if_fail (opt != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+
+	/* Allow everything */
+	if (!opt->str_allowed)
+		return TRUE;
+
+	candidates = g_strsplit (value, " ", 0);
+	if (!candidates)
+		goto out;
+
+	/* validate each space-separated word in 'value' */
+	for (candidate = candidates; *candidate; candidate++) {
+		found = FALSE;
+		for (allowed = (char **) opt->str_allowed; *allowed; allowed++) {
+			if (strcmp (*candidate, *allowed) == 0) {
+				found = TRUE;
+				break;
+			}
+		}
+		if (!found)
+			break;
+	}
+
+out:
+	g_strfreev (candidates);
+	return found;
+}
+
+OptType
+nm_supplicant_settings_verify_setting (const char * key,
+                                       const char * value,
+                                       const guint32 len)
+{
+	OptType type = TYPE_INVALID;
+	int opt_count = sizeof (opt_table) / sizeof (opt_table[0]);
+	int val_count = sizeof (validate_table) / sizeof (validate_table[0]);
+	int i, j;
+
+	g_return_val_if_fail (key != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+
+	for (i = 0; i < opt_count; i++) {
+		if (strcmp (opt_table[i].key, key) != 0)
+			continue;
+
+		for (j = 0; j < val_count; j++) {
+			if (validate_table[j].type == opt_table[i].type) {
+				if ((*(validate_table[j].func))(&opt_table[i], value, len)) {
+					type = opt_table[i].type;
+					break;
+				}
+			}
+		}
+	}
+
+	return type;
+}
+
diff --git a/src/supplicant-manager/nm-supplicant-settings-verify.h b/src/supplicant-manager/nm-supplicant-settings-verify.h
new file mode 100644
index 00000000..0939ebdb
--- /dev/null
+++ b/src/supplicant-manager/nm-supplicant-settings-verify.h
@@ -0,0 +1,37 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_SUPPLICANT_SETTINGS_VERIFY_H
+#define NM_SUPPLICANT_SETTINGS_VERIFY_H
+
+typedef enum OptType {
+	TYPE_INVALID = 0,
+	TYPE_INT,
+	TYPE_BYTES,
+	TYPE_KEYWORD,
+	TYPE_STRING
+} OptType;
+
+OptType nm_supplicant_settings_verify_setting (const char * key,
+                                               const char * value,
+                                               const guint32 len);
+
+
+#endif /* NM_SUPPLICANT_SETTINGS_VERIFY_H */
diff --git a/src/supplicant-manager/nm-supplicant-types.h b/src/supplicant-manager/nm-supplicant-types.h
new file mode 100644
index 00000000..16e4a6ba
--- /dev/null
+++ b/src/supplicant-manager/nm-supplicant-types.h
@@ -0,0 +1,28 @@
+/* -*- 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) 2006 - 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_SUPPLICANT_TYPES_H
+#define NM_SUPPLICANT_TYPES_H
+
+typedef struct _NMSupplicantManager NMSupplicantManager;
+typedef struct _NMSupplicantInterface NMSupplicantInterface;
+typedef struct _NMSupplicantConfig NMSupplicantConfig;
+
+#endif  /* NM_SUPPLICANT_TYPES_H */
diff --git a/src/supplicant-manager/tests/Makefile.am b/src/supplicant-manager/tests/Makefile.am
new file mode 100644
index 00000000..b5e4db61
--- /dev/null
+++ b/src/supplicant-manager/tests/Makefile.am
@@ -0,0 +1,27 @@
+INCLUDES = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/libnm-util \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/supplicant-manager
+
+noinst_PROGRAMS = test-supplicant-config
+
+test_supplicant_config_SOURCES = \
+	test-supplicant-config.c
+
+test_supplicant_config_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_supplicant_config_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/src/supplicant-manager/libsupplicant-manager.la \
+	$(DBUS_LIBS)
+
+if WITH_TESTS
+
+check-local: test-supplicant-config
+	$(abs_builddir)/test-supplicant-config
+
+endif
+
diff --git a/src/supplicant-manager/tests/Makefile.in b/src/supplicant-manager/tests/Makefile.in
new file mode 100644
index 00000000..f919b8b8
--- /dev/null
+++ b/src/supplicant-manager/tests/Makefile.in
@@ -0,0 +1,579 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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@
+noinst_PROGRAMS = test-supplicant-config$(EXEEXT)
+subdir = src/supplicant-manager/tests
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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_test_supplicant_config_OBJECTS =  \
+	test_supplicant_config-test-supplicant-config.$(OBJEXT)
+test_supplicant_config_OBJECTS = $(am_test_supplicant_config_OBJECTS)
+am__DEPENDENCIES_1 =
+test_supplicant_config_DEPENDENCIES =  \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/src/supplicant-manager/libsupplicant-manager.la \
+	$(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(test_supplicant_config_SOURCES)
+DIST_SOURCES = $(test_supplicant_config_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/libnm-util \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/supplicant-manager
+
+test_supplicant_config_SOURCES = \
+	test-supplicant-config.c
+
+test_supplicant_config_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_supplicant_config_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/src/supplicant-manager/libsupplicant-manager.la \
+	$(DBUS_LIBS)
+
+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/supplicant-manager/tests/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/supplicant-manager/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
+test-supplicant-config$(EXEEXT): $(test_supplicant_config_OBJECTS) $(test_supplicant_config_DEPENDENCIES) 
+	@rm -f test-supplicant-config$(EXEEXT)
+	$(LINK) $(test_supplicant_config_OBJECTS) $(test_supplicant_config_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_supplicant_config-test-supplicant-config.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+test_supplicant_config-test-supplicant-config.o: test-supplicant-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_supplicant_config_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_supplicant_config-test-supplicant-config.o -MD -MP -MF $(DEPDIR)/test_supplicant_config-test-supplicant-config.Tpo -c -o test_supplicant_config-test-supplicant-config.o `test -f 'test-supplicant-config.c' || echo '$(srcdir)/'`test-supplicant-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_supplicant_config-test-supplicant-config.Tpo $(DEPDIR)/test_supplicant_config-test-supplicant-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-supplicant-config.c' object='test_supplicant_config-test-supplicant-config.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_supplicant_config_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_supplicant_config-test-supplicant-config.o `test -f 'test-supplicant-config.c' || echo '$(srcdir)/'`test-supplicant-config.c
+
+test_supplicant_config-test-supplicant-config.obj: test-supplicant-config.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_supplicant_config_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_supplicant_config-test-supplicant-config.obj -MD -MP -MF $(DEPDIR)/test_supplicant_config-test-supplicant-config.Tpo -c -o test_supplicant_config-test-supplicant-config.obj `if test -f 'test-supplicant-config.c'; then $(CYGPATH_W) 'test-supplicant-config.c'; else $(CYGPATH_W) '$(srcdir)/test-supplicant-config.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_supplicant_config-test-supplicant-config.Tpo $(DEPDIR)/test_supplicant_config-test-supplicant-config.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-supplicant-config.c' object='test_supplicant_config-test-supplicant-config.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_supplicant_config_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_supplicant_config-test-supplicant-config.obj `if test -f 'test-supplicant-config.c'; then $(CYGPATH_W) 'test-supplicant-config.c'; else $(CYGPATH_W) '$(srcdir)/test-supplicant-config.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+@WITH_TESTS_FALSE@check-local:
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-local
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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 all all-am check check-am check-local clean \
+	clean-generic clean-libtool clean-noinstPROGRAMS ctags \
+	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 uninstall uninstall-am
+
+
+@WITH_TESTS_TRUE@check-local: test-supplicant-config
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-supplicant-config
+
+# 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/supplicant-manager/tests/test-supplicant-config.c b/src/supplicant-manager/tests/test-supplicant-config.c
new file mode 100644
index 00000000..d0868b5f
--- /dev/null
+++ b/src/supplicant-manager/tests/test-supplicant-config.c
@@ -0,0 +1,518 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 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) 2008 - 2009 Red Hat, Inc.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <dbus/dbus-glib.h>
+
+#include <nm-utils.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-wireless-security.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-8021x.h>
+
+#include "nm-test-helpers.h"
+
+#include "nm-supplicant-config.h"
+#include "nm-supplicant-settings-verify.h"
+
+static gboolean
+validate_opt (const char *detail,
+              GHashTable *hash,
+              const char *key,
+              OptType val_type,
+              gconstpointer expected,
+              size_t expected_len)
+{
+	GValue *value;
+	gint int_val;
+	GByteArray *array;
+	const char *s;
+	const unsigned char *expected_array = expected;
+	int result;
+
+	ASSERT (hash != NULL, detail, "hash was NULL");
+
+	value = g_hash_table_lookup (hash, key);
+	ASSERT (value != NULL,
+	        detail, "option '%s' expected but not found in config hash.");
+
+	switch (val_type) {
+	case TYPE_INT:
+		ASSERT (G_VALUE_HOLDS_INT (value),
+		        detail, "config hash item '%s' was not TYPE_INT.", key);
+		int_val = g_value_get_int (value);
+		ASSERT (int_val == GPOINTER_TO_INT (expected),
+		        detail, "unexpected config hash item '%s' value %d (expected %d)",
+		        key, int_val, GPOINTER_TO_INT (expected));
+		break;
+	case TYPE_BYTES:
+		ASSERT (G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY),
+		        detail, "config hash item '%s' was not TYPE_BYTES.", key);
+		array = g_value_get_boxed (value);
+		ASSERT (array->len == expected_len,
+		        detail, "unexpected config hash item '%s' length %d (expected %d)",
+		        key, array->len, expected_len);
+		result = memcmp (array->data, expected_array, expected_len);
+		ASSERT (result == 0, detail, "unexpected config hash item '%s' value", key);
+		break;
+	case TYPE_KEYWORD:
+	case TYPE_STRING:
+		ASSERT (G_VALUE_HOLDS_STRING (value),
+		        detail, "config hash item '%s' was not TYPE_STRING or TYPE_KEYWORD.", key);
+		if (expected_len == -1)
+			expected_len = strlen ((const char *) expected);
+		s = g_value_get_string (value);
+		ASSERT (s != NULL, detail, "unexpected NULL config hash string item '%s'.", key);
+		ASSERT (strlen (s) == expected_len,
+		        detail, "unexpected config hash string item '%s' length %d (expected %d)",
+		        key, strlen (s), expected_len);
+		result = strcmp (s, (const char *) expected);
+		ASSERT (result == 0,
+		        detail, "unexpected config hash string item '%s' value '%s' (expected '%s')",
+		        key, s, (const char *) expected);
+		break;
+	default:
+		g_warning ("unknown supplicant config hash item '%s' option type %d",
+		           key, val_type);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+test_wifi_open (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wifi;
+	NMSettingIP4Config *s_ip4;
+	NMSupplicantConfig *config;
+	GHashTable *hash;
+	char *uuid;
+	gboolean success;
+	GError *error = NULL;
+	GByteArray *ssid;
+	const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 };
+	GByteArray *bssid;
+	const unsigned char bssid_data[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+	const char *bssid_str = "11:22:33:44:55:66";
+
+	connection = nm_connection_new ();
+	ASSERT (connection != NULL,
+	        "wifi-open", "failed to allocate new connection");
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	ASSERT (s_con != NULL,
+	        "wifi-open", "failed to allocate new %s setting",
+	        NM_SETTING_CONNECTION_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Wifi Open",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wifi setting */
+	s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+	ASSERT (s_wifi != NULL,
+	        "wifi-open", "failed to allocate new %s setting",
+	        NM_SETTING_WIRELESS_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+	ssid = g_byte_array_sized_new (sizeof (ssid_data));
+	g_byte_array_append (ssid, ssid_data, sizeof (ssid_data));
+	bssid = g_byte_array_sized_new (sizeof (bssid_data));
+	g_byte_array_append (bssid, bssid_data, sizeof (bssid_data));
+
+	g_object_set (s_wifi,
+	              NM_SETTING_WIRELESS_SSID, ssid,
+	              NM_SETTING_WIRELESS_BSSID, bssid,
+	              NM_SETTING_WIRELESS_MODE, "infrastructure",
+	              NM_SETTING_WIRELESS_BAND, "bg",
+	              NULL);
+
+	g_byte_array_free (ssid, TRUE);
+	g_byte_array_free (bssid, TRUE);
+
+	/* IP4 setting */
+	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+	ASSERT (s_ip4 != NULL,
+	        "wifi-open", "failed to allocate new %s setting",
+	        NM_SETTING_IP4_CONFIG_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+	ASSERT (nm_connection_verify (connection, &error) == TRUE,
+	        "wifi-open", "failed to verify connection: %s",
+	        (error && error->message) ? error->message : "(unknown)");
+
+	config = nm_supplicant_config_new ();
+	ASSERT (config != NULL,
+	        "wifi-open", "failed to create new supplicant config");
+
+	success = nm_supplicant_config_add_setting_wireless (config, s_wifi, TRUE, 0, TRUE);
+	ASSERT (success == TRUE,
+	        "wifi-open", "failed to add wireless setting to supplicant config.");
+
+	success = nm_supplicant_config_add_no_security (config);
+	ASSERT (success == TRUE,
+	        "wifi-open", "failed to add wireless security to supplicant config.");
+
+	hash = nm_supplicant_config_get_hash (config);
+	ASSERT (hash != NULL,
+	        "wifi-open", "failed to hash supplicant config options.");
+
+	validate_opt ("wifi-open", hash, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1);
+	validate_opt ("wifi-open", hash, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data));
+	validate_opt ("wifi-open", hash, "bssid", TYPE_KEYWORD, bssid_str, -1);
+	validate_opt ("wifi-open", hash, "key_mgmt", TYPE_KEYWORD, "NONE", -1);
+
+	g_object_unref (connection);
+}
+
+static void
+test_wifi_wep_key (const char *detail,
+                   NMWepKeyType wep_type,
+                   const char *key_data,
+                   const unsigned char *expected,
+                   size_t expected_size)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	NMSettingIP4Config *s_ip4;
+	NMSupplicantConfig *config;
+	GHashTable *hash;
+	char *uuid;
+	gboolean success;
+	GError *error = NULL;
+	GByteArray *ssid;
+	const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 };
+	GByteArray *bssid;
+	const unsigned char bssid_data[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+	const char *bssid_str = "11:22:33:44:55:66";
+
+	connection = nm_connection_new ();
+	ASSERT (connection != NULL,
+	        detail, "failed to allocate new connection");
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	ASSERT (s_con != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_CONNECTION_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Wifi WEP Key",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wifi setting */
+	s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+	ASSERT (s_wifi != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_WIRELESS_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+	ssid = g_byte_array_sized_new (sizeof (ssid_data));
+	g_byte_array_append (ssid, ssid_data, sizeof (ssid_data));
+	bssid = g_byte_array_sized_new (sizeof (bssid_data));
+	g_byte_array_append (bssid, bssid_data, sizeof (bssid_data));
+
+	g_object_set (s_wifi,
+	              NM_SETTING_WIRELESS_SSID, ssid,
+	              NM_SETTING_WIRELESS_BSSID, bssid,
+	              NM_SETTING_WIRELESS_MODE, "infrastructure",
+	              NM_SETTING_WIRELESS_BAND, "bg",
+	              NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+	              NULL);
+
+	g_byte_array_free (ssid, TRUE);
+	g_byte_array_free (bssid, TRUE);
+
+	/* Wifi Security setting */
+	s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+	ASSERT (s_wsec != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+
+	g_object_set (s_wsec,
+	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none",
+	              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, wep_type,
+	              NULL);
+	nm_setting_wireless_security_set_wep_key (s_wsec, 0, key_data);	
+
+	/* IP4 setting */
+	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+	ASSERT (s_ip4 != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_IP4_CONFIG_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+	ASSERT (nm_connection_verify (connection, &error) == TRUE,
+	        detail, "failed to verify connection: %s",
+	        (error && error->message) ? error->message : "(unknown)");
+
+	config = nm_supplicant_config_new ();
+	ASSERT (config != NULL,
+	        detail, "failed to create new supplicant config");
+
+	success = nm_supplicant_config_add_setting_wireless (config, s_wifi, TRUE, 0, TRUE);
+	ASSERT (success == TRUE,
+	        detail, "failed to add wireless setting to supplicant config.");
+
+	success = nm_supplicant_config_add_setting_wireless_security (config,
+	                                                              s_wsec,
+	                                                              NULL,
+	                                                              "376aced7-b28c-46be-9a62-fcdf072571da");
+	ASSERT (success == TRUE,
+	        detail, "failed to add wireless security to supplicant config.");
+
+	hash = nm_supplicant_config_get_hash (config);
+	ASSERT (hash != NULL,
+	        detail, "failed to hash supplicant config options.");
+
+	validate_opt (detail, hash, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1);
+	validate_opt (detail, hash, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data));
+	validate_opt (detail, hash, "bssid", TYPE_KEYWORD, bssid_str, -1);
+	validate_opt (detail, hash, "key_mgmt", TYPE_KEYWORD, "NONE", -1);
+	validate_opt (detail, hash, "wep_tx_keyidx", TYPE_INT, GINT_TO_POINTER (0), -1);
+	validate_opt (detail, hash, "wep_key0", TYPE_BYTES, expected, expected_size);
+
+	g_object_unref (connection);
+}
+
+static void
+test_wifi_wep (void)
+{
+	const char *key1 = "12345";
+	const unsigned char key1_expected[] = { 0x31, 0x32, 0x33, 0x34, 0x35 };
+	const char *key2 = "ascii test$$$";
+	const unsigned char key2_expected[] = { 0x61, 0x73, 0x63, 0x69, 0x69, 0x20, 0x74, 0x65, 0x73, 0x74, 0x24, 0x24, 0x24 };
+	const char *key3 = "abcdef1234";
+	const unsigned char key3_expected[] = { 0xab, 0xcd, 0xef, 0x12, 0x34 };
+	const char *key4 = "96aec785c6392675f87f592972";
+	const unsigned char key4_expected[] = { 0x96, 0xae, 0xc7, 0x85, 0xc6, 0x39, 0x26, 0x75, 0xf8, 0x7f, 0x59, 0x29, 0x72 };
+	const char *key5 = "r34lly l33t w3p p4ssphr4s3 for t3st1ng";
+	const unsigned char key5_expected[] = { 0xce, 0x68, 0x8b, 0x35, 0xf6, 0x0a, 0x2b, 0xbf, 0xc9, 0x8f, 0xed, 0x10, 0xda };
+
+	test_wifi_wep_key ("wifi-wep-ascii-40", NM_WEP_KEY_TYPE_KEY, key1, key1_expected, sizeof (key1_expected));
+	test_wifi_wep_key ("wifi-wep-ascii-104", NM_WEP_KEY_TYPE_KEY, key2, key2_expected, sizeof (key2_expected));
+	test_wifi_wep_key ("wifi-wep-hex-40", NM_WEP_KEY_TYPE_KEY, key3, key3_expected, sizeof (key3_expected));
+	test_wifi_wep_key ("wifi-wep-hex-104", NM_WEP_KEY_TYPE_KEY, key4, key4_expected, sizeof (key4_expected));
+	test_wifi_wep_key ("wifi-wep-passphrase-104", NM_WEP_KEY_TYPE_PASSPHRASE, key5, key5_expected, sizeof (key5_expected));
+
+	test_wifi_wep_key ("wifi-wep-old-hex-104", NM_WEP_KEY_TYPE_UNKNOWN, key4, key4_expected, sizeof (key4_expected));
+}
+
+static void
+test_wifi_wpa_psk (const char *detail,
+                   OptType key_type,
+                   const char *key_data,
+                   const unsigned char *expected,
+                   size_t expected_size)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	NMSettingIP4Config *s_ip4;
+	NMSupplicantConfig *config;
+	GHashTable *hash;
+	char *uuid;
+	gboolean success;
+	GError *error = NULL;
+	GByteArray *ssid;
+	const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 };
+	GByteArray *bssid;
+	const unsigned char bssid_data[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+	const char *bssid_str = "11:22:33:44:55:66";
+
+	connection = nm_connection_new ();
+	ASSERT (connection != NULL,
+	        detail, "failed to allocate new connection");
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	ASSERT (s_con != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_CONNECTION_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Wifi WEP Key",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wifi setting */
+	s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+	ASSERT (s_wifi != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_WIRELESS_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+	ssid = g_byte_array_sized_new (sizeof (ssid_data));
+	g_byte_array_append (ssid, ssid_data, sizeof (ssid_data));
+	bssid = g_byte_array_sized_new (sizeof (bssid_data));
+	g_byte_array_append (bssid, bssid_data, sizeof (bssid_data));
+
+	g_object_set (s_wifi,
+	              NM_SETTING_WIRELESS_SSID, ssid,
+	              NM_SETTING_WIRELESS_BSSID, bssid,
+	              NM_SETTING_WIRELESS_MODE, "infrastructure",
+	              NM_SETTING_WIRELESS_BAND, "bg",
+	              NM_SETTING_WIRELESS_SEC, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+	              NULL);
+
+	g_byte_array_free (ssid, TRUE);
+	g_byte_array_free (bssid, TRUE);
+
+	/* Wifi Security setting */
+	s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+	ASSERT (s_wsec != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+
+	g_object_set (s_wsec,
+	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
+	              NM_SETTING_WIRELESS_SECURITY_PSK, key_data,
+	              NULL);
+
+	nm_setting_wireless_security_add_proto (s_wsec, "wpa");
+	nm_setting_wireless_security_add_proto (s_wsec, "rsn");
+	nm_setting_wireless_security_add_pairwise (s_wsec, "tkip");
+	nm_setting_wireless_security_add_pairwise (s_wsec, "ccmp");
+	nm_setting_wireless_security_add_group (s_wsec, "tkip");
+	nm_setting_wireless_security_add_group (s_wsec, "ccmp");
+
+	/* IP4 setting */
+	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+	ASSERT (s_ip4 != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_IP4_CONFIG_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+	ASSERT (nm_connection_verify (connection, &error) == TRUE,
+	        detail, "failed to verify connection: %s",
+	        (error && error->message) ? error->message : "(unknown)");
+
+	config = nm_supplicant_config_new ();
+	ASSERT (config != NULL,
+	        detail, "failed to create new supplicant config");
+
+	success = nm_supplicant_config_add_setting_wireless (config, s_wifi, TRUE, 0, TRUE);
+	ASSERT (success == TRUE,
+	        detail, "failed to add wireless setting to supplicant config.");
+
+	success = nm_supplicant_config_add_setting_wireless_security (config,
+	                                                              s_wsec,
+	                                                              NULL,
+	                                                              "376aced7-b28c-46be-9a62-fcdf072571da");
+	ASSERT (success == TRUE,
+	        detail, "failed to add wireless security to supplicant config.");
+
+	hash = nm_supplicant_config_get_hash (config);
+	ASSERT (hash != NULL,
+	        detail, "failed to hash supplicant config options.");
+
+	validate_opt (detail, hash, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1);
+	validate_opt (detail, hash, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data));
+	validate_opt (detail, hash, "bssid", TYPE_KEYWORD, bssid_str, -1);
+	validate_opt (detail, hash, "key_mgmt", TYPE_KEYWORD, "WPA-PSK", -1);
+	validate_opt (detail, hash, "proto", TYPE_KEYWORD, "WPA RSN", -1);
+	validate_opt (detail, hash, "pairwise", TYPE_KEYWORD, "TKIP CCMP", -1);
+	validate_opt (detail, hash, "group", TYPE_KEYWORD, "TKIP CCMP", -1);
+	validate_opt (detail, hash, "psk", key_type, expected, expected_size);
+
+	g_object_unref (connection);
+}
+
+static void
+test_wifi_wpa_psk_types (void)
+{
+	const char *key1 = "d4721e911461d3cdef9793858e977fcda091779243abb7316c2f11605a160893";
+	const unsigned char key1_expected[] = { 0xd4, 0x72, 0x1e, 0x91, 0x14, 0x61, 0xd3, 0xcd,
+	                                        0xef, 0x97, 0x93, 0x85, 0x8e, 0x97, 0x7f, 0xcd,
+	                                        0xa0, 0x91, 0x77, 0x92, 0x43, 0xab, 0xb7, 0x31,
+	                                        0x6c, 0x2f, 0x11, 0x60, 0x5a, 0x16, 0x08, 0x93 };
+	const char *key2 = "r34lly l33t wp4 p4ssphr4s3 for t3st1ng";
+
+	test_wifi_wpa_psk ("wifi-wpa-psk-hex", TYPE_BYTES, key1, key1_expected, sizeof (key1_expected));
+	test_wifi_wpa_psk ("wifi-wep-psk-passphrase", TYPE_STRING, key2, (gconstpointer) key2, strlen (key2));
+}
+
+int main (int argc, char **argv)
+{
+	GError *error = NULL;
+	DBusGConnection *bus;
+	char *base;
+
+	g_type_init ();
+	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+
+	if (!nm_utils_init (&error))
+		FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+
+	/* The tests */
+	test_wifi_open ();
+	test_wifi_wep ();
+	test_wifi_wpa_psk_types ();
+
+	base = g_path_get_basename (argv[0]);
+	fprintf (stdout, "%s: SUCCESS\n", base);
+	g_free (base);
+	return 0;
+}
+
diff --git a/src/system-settings/Makefile.am b/src/system-settings/Makefile.am
new file mode 100644
index 00000000..3b616b90
--- /dev/null
+++ b/src/system-settings/Makefile.am
@@ -0,0 +1,61 @@
+INCLUDES = -I${top_srcdir} \
+           -I${top_srcdir}/include \
+           -I${top_srcdir}/libnm-util \
+           -I${top_srcdir}/libnm-glib \
+           -I${top_srcdir}/src/logging \
+           -I${top_srcdir}/src \
+           -I${top_builddir}/marshallers
+
+noinst_LTLIBRARIES = libsystem-settings.la
+
+BUILT_SOURCES = \
+	nm-settings-system-glue.h
+
+libsystem_settings_la_SOURCES = \
+	nm-sysconfig-settings.c \
+	nm-sysconfig-settings.h \
+	nm-inotify-helper.c \
+	nm-inotify-helper.h \
+	nm-polkit-helpers.h \
+	nm-system-config-error.c \
+	nm-system-config-error.h \
+	nm-system-config-interface.c \
+	nm-system-config-interface.h \
+	nm-sysconfig-connection.c \
+	nm-sysconfig-connection.h \
+	nm-default-wired-connection.c \
+	nm-default-wired-connection.h
+
+libsystem_settings_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(GMODULE_CFLAGS) \
+	$(POLKIT_CFLAGS) \
+	-DG_DISABLE_DEPRECATED	\
+	-DBINDIR=\"$(bindir)\" \
+	-DSBINDIR=\"$(sbindir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
+	-DGNOMELOCALEDIR=\"$(datadir)/locale\" \
+	-DPLUGINDIR=\"$(pkglibdir)\"
+
+libsystem_settings_la_LIBADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/libnm-glib/libnm-glib.la \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS) \
+	$(GMODULE_LIBS) \
+	$(POLKIT_LIBS)
+
+libsystem_settings_la_LDFLAGS = -rdynamic
+
+nm-settings-system-glue.h: $(top_srcdir)/introspection/nm-settings-system.xml
+	dbus-binding-tool --prefix=nm_settings_system --mode=glib-server --output=$@ $<
+
+CLEANFILES = \
+	$(BUILT_SOURCES)
+
diff --git a/src/system-settings/Makefile.in b/src/system-settings/Makefile.in
new file mode 100644
index 00000000..d86d4188
--- /dev/null
+++ b/src/system-settings/Makefile.in
@@ -0,0 +1,661 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/system-settings
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libsystem_settings_la_DEPENDENCIES =  \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/libnm-glib/libnm-glib.la \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libsystem_settings_la_OBJECTS =  \
+	libsystem_settings_la-nm-sysconfig-settings.lo \
+	libsystem_settings_la-nm-inotify-helper.lo \
+	libsystem_settings_la-nm-system-config-error.lo \
+	libsystem_settings_la-nm-system-config-interface.lo \
+	libsystem_settings_la-nm-sysconfig-connection.lo \
+	libsystem_settings_la-nm-default-wired-connection.lo
+libsystem_settings_la_OBJECTS = $(am_libsystem_settings_la_OBJECTS)
+libsystem_settings_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(libsystem_settings_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libsystem_settings_la_SOURCES)
+DIST_SOURCES = $(libsystem_settings_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = -I${top_srcdir} \
+           -I${top_srcdir}/include \
+           -I${top_srcdir}/libnm-util \
+           -I${top_srcdir}/libnm-glib \
+           -I${top_srcdir}/src/logging \
+           -I${top_srcdir}/src \
+           -I${top_builddir}/marshallers
+
+noinst_LTLIBRARIES = libsystem-settings.la
+BUILT_SOURCES = \
+	nm-settings-system-glue.h
+
+libsystem_settings_la_SOURCES = \
+	nm-sysconfig-settings.c \
+	nm-sysconfig-settings.h \
+	nm-inotify-helper.c \
+	nm-inotify-helper.h \
+	nm-polkit-helpers.h \
+	nm-system-config-error.c \
+	nm-system-config-error.h \
+	nm-system-config-interface.c \
+	nm-system-config-interface.h \
+	nm-sysconfig-connection.c \
+	nm-sysconfig-connection.h \
+	nm-default-wired-connection.c \
+	nm-default-wired-connection.h
+
+libsystem_settings_la_CPPFLAGS = \
+	$(DBUS_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(GMODULE_CFLAGS) \
+	$(POLKIT_CFLAGS) \
+	-DG_DISABLE_DEPRECATED	\
+	-DBINDIR=\"$(bindir)\" \
+	-DSBINDIR=\"$(sbindir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
+	-DGNOMELOCALEDIR=\"$(datadir)/locale\" \
+	-DPLUGINDIR=\"$(pkglibdir)\"
+
+libsystem_settings_la_LIBADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/libnm-glib/libnm-glib.la \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS) \
+	$(GMODULE_LIBS) \
+	$(POLKIT_LIBS)
+
+libsystem_settings_la_LDFLAGS = -rdynamic
+CLEANFILES = \
+	$(BUILT_SOURCES)
+
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) 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/system-settings/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/system-settings/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libsystem-settings.la: $(libsystem_settings_la_OBJECTS) $(libsystem_settings_la_DEPENDENCIES) 
+	$(libsystem_settings_la_LINK)  $(libsystem_settings_la_OBJECTS) $(libsystem_settings_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-default-wired-connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-inotify-helper.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-sysconfig-connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-sysconfig-settings.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-system-config-error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystem_settings_la-nm-system-config-interface.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libsystem_settings_la-nm-sysconfig-settings.lo: nm-sysconfig-settings.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-sysconfig-settings.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-sysconfig-settings.Tpo -c -o libsystem_settings_la-nm-sysconfig-settings.lo `test -f 'nm-sysconfig-settings.c' || echo '$(srcdir)/'`nm-sysconfig-settings.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsystem_settings_la-nm-sysconfig-settings.Tpo $(DEPDIR)/libsystem_settings_la-nm-sysconfig-settings.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-sysconfig-settings.c' object='libsystem_settings_la-nm-sysconfig-settings.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-sysconfig-settings.lo `test -f 'nm-sysconfig-settings.c' || echo '$(srcdir)/'`nm-sysconfig-settings.c
+
+libsystem_settings_la-nm-inotify-helper.lo: nm-inotify-helper.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-inotify-helper.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-inotify-helper.Tpo -c -o libsystem_settings_la-nm-inotify-helper.lo `test -f 'nm-inotify-helper.c' || echo '$(srcdir)/'`nm-inotify-helper.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsystem_settings_la-nm-inotify-helper.Tpo $(DEPDIR)/libsystem_settings_la-nm-inotify-helper.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-inotify-helper.c' object='libsystem_settings_la-nm-inotify-helper.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-inotify-helper.lo `test -f 'nm-inotify-helper.c' || echo '$(srcdir)/'`nm-inotify-helper.c
+
+libsystem_settings_la-nm-system-config-error.lo: nm-system-config-error.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-system-config-error.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-system-config-error.Tpo -c -o libsystem_settings_la-nm-system-config-error.lo `test -f 'nm-system-config-error.c' || echo '$(srcdir)/'`nm-system-config-error.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsystem_settings_la-nm-system-config-error.Tpo $(DEPDIR)/libsystem_settings_la-nm-system-config-error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-system-config-error.c' object='libsystem_settings_la-nm-system-config-error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-system-config-error.lo `test -f 'nm-system-config-error.c' || echo '$(srcdir)/'`nm-system-config-error.c
+
+libsystem_settings_la-nm-system-config-interface.lo: nm-system-config-interface.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-system-config-interface.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-system-config-interface.Tpo -c -o libsystem_settings_la-nm-system-config-interface.lo `test -f 'nm-system-config-interface.c' || echo '$(srcdir)/'`nm-system-config-interface.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsystem_settings_la-nm-system-config-interface.Tpo $(DEPDIR)/libsystem_settings_la-nm-system-config-interface.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-system-config-interface.c' object='libsystem_settings_la-nm-system-config-interface.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-system-config-interface.lo `test -f 'nm-system-config-interface.c' || echo '$(srcdir)/'`nm-system-config-interface.c
+
+libsystem_settings_la-nm-sysconfig-connection.lo: nm-sysconfig-connection.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-sysconfig-connection.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-sysconfig-connection.Tpo -c -o libsystem_settings_la-nm-sysconfig-connection.lo `test -f 'nm-sysconfig-connection.c' || echo '$(srcdir)/'`nm-sysconfig-connection.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsystem_settings_la-nm-sysconfig-connection.Tpo $(DEPDIR)/libsystem_settings_la-nm-sysconfig-connection.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-sysconfig-connection.c' object='libsystem_settings_la-nm-sysconfig-connection.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-sysconfig-connection.lo `test -f 'nm-sysconfig-connection.c' || echo '$(srcdir)/'`nm-sysconfig-connection.c
+
+libsystem_settings_la-nm-default-wired-connection.lo: nm-default-wired-connection.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystem_settings_la-nm-default-wired-connection.lo -MD -MP -MF $(DEPDIR)/libsystem_settings_la-nm-default-wired-connection.Tpo -c -o libsystem_settings_la-nm-default-wired-connection.lo `test -f 'nm-default-wired-connection.c' || echo '$(srcdir)/'`nm-default-wired-connection.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libsystem_settings_la-nm-default-wired-connection.Tpo $(DEPDIR)/libsystem_settings_la-nm-default-wired-connection.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-default-wired-connection.c' object='libsystem_settings_la-nm-default-wired-connection.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystem_settings_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystem_settings_la-nm-default-wired-connection.lo `test -f 'nm-default-wired-connection.c' || echo '$(srcdir)/'`nm-default-wired-connection.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) 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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+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."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+	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: all check install install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 uninstall uninstall-am
+
+
+nm-settings-system-glue.h: $(top_srcdir)/introspection/nm-settings-system.xml
+	dbus-binding-tool --prefix=nm_settings_system --mode=glib-server --output=$@ $<
+
+# 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/system-settings/nm-default-wired-connection.c b/src/system-settings/nm-default-wired-connection.c
new file mode 100644
index 00000000..54e00d62
--- /dev/null
+++ b/src/system-settings/nm-default-wired-connection.c
@@ -0,0 +1,308 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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.
+ *
+ * (C) Copyright 2008 Novell, Inc.
+ * (C) Copyright 2009 Red Hat, Inc.
+ */
+
+#include <netinet/ether.h>
+
+#include <glib/gi18n.h>
+
+#include <NetworkManager.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-utils.h>
+
+#include "nm-dbus-glib-types.h"
+#include "nm-marshal.h"
+#include "nm-default-wired-connection.h"
+#include "nm-settings-connection-interface.h"
+
+static NMSettingsConnectionInterface *parent_settings_connection_iface;
+
+static void settings_connection_interface_init (NMSettingsConnectionInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (NMDefaultWiredConnection, nm_default_wired_connection, NM_TYPE_SYSCONFIG_CONNECTION, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_CONNECTION_INTERFACE,
+                                               settings_connection_interface_init))
+
+#define NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionPrivate))
+
+typedef struct {
+	NMDevice *device;
+	GByteArray *mac;
+	gboolean read_only;
+} NMDefaultWiredConnectionPrivate;
+
+enum {
+	PROP_0,
+	PROP_MAC,
+	PROP_DEVICE,
+	PROP_READ_ONLY,
+	LAST_PROP
+};
+
+enum {
+	TRY_UPDATE,
+	DELETED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+NMDefaultWiredConnection *
+nm_default_wired_connection_new (const GByteArray *mac,
+                                 NMDevice *device,
+                                 gboolean read_only)
+{
+
+	g_return_val_if_fail (mac != NULL, NULL);
+	g_return_val_if_fail (mac->len == ETH_ALEN, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+	return g_object_new (NM_TYPE_DEFAULT_WIRED_CONNECTION,
+	                     NM_DEFAULT_WIRED_CONNECTION_MAC, mac,
+	                     NM_DEFAULT_WIRED_CONNECTION_DEVICE, device,
+	                     NM_DEFAULT_WIRED_CONNECTION_READ_ONLY, read_only,
+	                     NULL);
+}
+
+NMDevice *
+nm_default_wired_connection_get_device (NMDefaultWiredConnection *wired)
+{
+	g_return_val_if_fail (NM_IS_DEFAULT_WIRED_CONNECTION (wired), NULL);
+
+	return NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (wired)->device;
+}
+
+static gboolean
+update (NMSettingsConnectionInterface *connection,
+	    NMSettingsConnectionInterfaceUpdateFunc callback,
+	    gpointer user_data)
+{
+	NMDefaultWiredConnection *self = NM_DEFAULT_WIRED_CONNECTION (connection);
+
+	/* Keep the object alive over try-update since it might get removed
+	 * from the settings service there, but we still need it for the callback.
+	 */
+	g_object_ref (connection);
+	g_signal_emit (self, signals[TRY_UPDATE], 0);
+	callback (connection, NULL, user_data);
+	g_object_unref (connection);
+	return TRUE;
+}
+
+static gboolean 
+do_delete (NMSettingsConnectionInterface *connection,
+	       NMSettingsConnectionInterfaceDeleteFunc callback,
+	       gpointer user_data)
+{
+	NMDefaultWiredConnection *self = NM_DEFAULT_WIRED_CONNECTION (connection);
+	NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (connection);
+
+	g_signal_emit (self, signals[DELETED], 0, priv->mac);
+	return parent_settings_connection_iface->delete (connection, callback, user_data);
+}
+
+/****************************************************************/
+
+static void
+settings_connection_interface_init (NMSettingsConnectionInterface *iface)
+{
+	parent_settings_connection_iface = g_type_interface_peek_parent (iface);
+	iface->update = update;
+	iface->delete = do_delete;
+}
+
+static void
+nm_default_wired_connection_init (NMDefaultWiredConnection *self)
+{
+}
+
+static GObject *
+constructor (GType type,
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	NMDefaultWiredConnectionPrivate *priv;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	char *id, *uuid;
+
+	object = G_OBJECT_CLASS (nm_default_wired_connection_parent_class)->constructor (type, n_construct_params, construct_params);
+	if (!object)
+		return NULL;
+
+	priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object);
+
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+
+	id = g_strdup_printf (_("Auto %s"), nm_device_get_iface (priv->device));
+	uuid = nm_utils_uuid_generate ();
+
+	g_object_set (s_con,
+		      NM_SETTING_CONNECTION_ID, id,
+		      NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+		      NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+		      NM_SETTING_CONNECTION_UUID, uuid,
+		      NM_SETTING_CONNECTION_READ_ONLY, priv->read_only,
+		      NULL);
+
+	g_free (id);
+	g_free (uuid);
+
+	nm_connection_add_setting (NM_CONNECTION (object), NM_SETTING (s_con));
+
+	/* Lock the connection to the specific device */
+	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
+	g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, priv->mac, NULL);
+	nm_connection_add_setting (NM_CONNECTION (object), NM_SETTING (s_wired));
+
+	return object;
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object);
+
+	g_object_unref (priv->device);
+	g_byte_array_free (priv->mac, TRUE);
+
+	G_OBJECT_CLASS (nm_default_wired_connection_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MAC:
+		g_value_set_pointer (value, priv->mac);
+		break;
+	case PROP_DEVICE:
+		g_value_set_object (value, priv->device);
+		break;
+	case PROP_READ_ONLY:
+		g_value_set_boolean (value, priv->read_only);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object);
+	GByteArray *array;
+
+	switch (prop_id) {
+	case PROP_MAC:
+		/* Construct only */
+		array = g_value_get_pointer (value);
+		if (priv->mac) {
+			g_byte_array_free (priv->mac, TRUE);
+			priv->mac = NULL;
+		}
+		if (array) {
+			g_return_if_fail (array->len == ETH_ALEN);
+			priv->mac = g_byte_array_sized_new (array->len);
+			g_byte_array_append (priv->mac, array->data, ETH_ALEN);
+		}
+		break;
+	case PROP_DEVICE:
+		if (priv->device)
+			g_object_unref (priv->device);
+		priv->device = g_value_dup_object (value);
+		break;
+	case PROP_READ_ONLY:
+		priv->read_only = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_default_wired_connection_class_init (NMDefaultWiredConnectionClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMDefaultWiredConnectionPrivate));
+
+	/* Virtual methods */
+	object_class->constructor = constructor;
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_MAC,
+		 g_param_spec_pointer (NM_DEFAULT_WIRED_CONNECTION_MAC,
+		                       "MAC",
+		                       "MAC Address",
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_DEVICE,
+		 g_param_spec_object (NM_DEFAULT_WIRED_CONNECTION_DEVICE,
+		                       "Device",
+		                       "Device",
+		                       NM_TYPE_DEVICE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_READ_ONLY,
+		 g_param_spec_boolean (NM_DEFAULT_WIRED_CONNECTION_READ_ONLY,
+		                       "ReadOnly",
+		                       "Read Only",
+		                       FALSE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/* Signals */
+	signals[TRY_UPDATE] =
+		g_signal_new ("try-update",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);
+
+	/* The 'deleted' signal is used to signal intentional deletions (like
+	 * updating or user-requested deletion) rather than using the
+	 * superclass' 'removed' signal, since that signal doesn't have the
+	 * semantics we want; it gets emitted as a side-effect of various operations
+	 * and is meant more for D-Bus clients instead of in-service uses.
+	 */
+	signals[DELETED] =
+		g_signal_new ("deleted",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL,
+			      g_cclosure_marshal_VOID__POINTER,
+			      G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
diff --git a/src/system-settings/nm-default-wired-connection.h b/src/system-settings/nm-default-wired-connection.h
new file mode 100644
index 00000000..de89b675
--- /dev/null
+++ b/src/system-settings/nm-default-wired-connection.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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.
+ *
+ * (C) Copyright 2008 Novell, Inc.
+ * (C) Copyright 2009 Red Hat, Inc.
+ */
+
+#ifndef NM_DEFAULT_WIRED_CONNECTION_H
+#define NM_DEFAULT_WIRED_CONNECTION_H
+
+#include "nm-sysconfig-connection.h"
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEFAULT_WIRED_CONNECTION            (nm_default_wired_connection_get_type ())
+#define NM_DEFAULT_WIRED_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnection))
+#define NM_DEFAULT_WIRED_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass))
+#define NM_IS_DEFAULT_WIRED_CONNECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION))
+#define NM_IS_DEFAULT_WIRED_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION))
+#define NM_DEFAULT_WIRED_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass))
+
+#define NM_DEFAULT_WIRED_CONNECTION_MAC   "mac"
+#define NM_DEFAULT_WIRED_CONNECTION_DEVICE "device"
+#define NM_DEFAULT_WIRED_CONNECTION_READ_ONLY "read-only"
+
+typedef struct {
+	NMSysconfigConnection parent;
+} NMDefaultWiredConnection;
+
+typedef struct {
+	NMSysconfigConnectionClass parent;
+} NMDefaultWiredConnectionClass;
+
+GType nm_default_wired_connection_get_type (void);
+
+NMDefaultWiredConnection *nm_default_wired_connection_new (const GByteArray *mac,
+                                                           NMDevice *device,
+                                                           gboolean read_only);
+
+NMDevice *nm_default_wired_connection_get_device (NMDefaultWiredConnection *wired);
+
+G_END_DECLS
+
+#endif /* NM_DEFAULT_WIRED_CONNECTION_H */
diff --git a/src/system-settings/nm-inotify-helper.c b/src/system-settings/nm-inotify-helper.c
new file mode 100644
index 00000000..0ee168e5
--- /dev/null
+++ b/src/system-settings/nm-inotify-helper.c
@@ -0,0 +1,212 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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.
+ *
+ * (C) Copyright 2008 Red Hat, Inc.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <glib.h>
+
+#include "nm-marshal.h"
+#include "nm-inotify-helper.h"
+#include "nm-logging.h"
+
+G_DEFINE_TYPE (NMInotifyHelper, nm_inotify_helper, G_TYPE_OBJECT)
+
+#define NM_INOTIFY_HELPER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperPrivate))
+
+typedef struct {
+	int ifd;
+
+	GHashTable *wd_refs;
+} NMInotifyHelperPrivate;
+
+/* Signals */
+enum {
+	EVENT,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+int
+nm_inotify_helper_add_watch (NMInotifyHelper *self, const char *path)
+{
+	NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self);
+	int wd;
+	guint32 refcount;
+
+	g_return_val_if_fail (priv->ifd >= 0, -1);
+
+	/* We only care about modifications since we're just trying to get change
+	 * notifications on hardlinks.
+	 */
+
+	wd = inotify_add_watch (priv->ifd, path, IN_CLOSE_WRITE);
+	if (wd < 0)
+		return -1;
+
+	refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd)));
+	refcount++;
+	g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount));
+
+	return wd;
+}
+
+void
+nm_inotify_helper_remove_watch (NMInotifyHelper *self, int wd)
+{
+	NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self);
+	guint32 refcount;
+
+	g_return_if_fail (priv->ifd >= 0);
+
+	refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd)));
+	if (!refcount)
+		return;
+
+	refcount--;
+	if (!refcount) {
+		g_hash_table_remove (priv->wd_refs, GINT_TO_POINTER (wd));
+		inotify_rm_watch (priv->ifd, wd);
+	} else
+		g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount));
+}
+
+static gboolean
+inotify_event_handler (GIOChannel *channel, GIOCondition cond, gpointer user_data)
+{
+	NMInotifyHelper *self = NM_INOTIFY_HELPER (user_data);
+	struct inotify_event evt;
+
+	/* read the notifications from the watch descriptor */
+	while (g_io_channel_read_chars (channel, (gchar *) &evt, sizeof (struct inotify_event), NULL, NULL) == G_IO_STATUS_NORMAL) {
+		gchar filename[PATH_MAX + 1];
+
+		filename[0] = '\0';
+		if (evt.len > 0) {
+			g_io_channel_read_chars (channel,
+			                        filename,
+			                        evt.len > PATH_MAX ? PATH_MAX : evt.len,
+			                        NULL, NULL);
+		}
+
+		if (!(evt.mask & IN_IGNORED))
+			g_signal_emit (self, signals[EVENT], 0, &evt, &filename[0]);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+init_inotify (NMInotifyHelper *self)
+{
+	NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self);
+	GIOChannel *channel;
+	guint source_id;
+
+	priv->ifd = inotify_init ();
+	if (priv->ifd == -1) {
+		nm_log_warn (LOGD_SYS_SET, "couldn't initialize inotify");
+		return FALSE;
+	}
+
+	/* Watch the inotify descriptor for file/directory change events */
+	channel = g_io_channel_unix_new (priv->ifd);
+	if (!channel) {
+		nm_log_warn (LOGD_SYS_SET, "couldn't create new GIOChannel");
+		close (priv->ifd);
+		priv->ifd = -1;
+		return FALSE;
+	}
+
+	g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
+	g_io_channel_set_encoding (channel, NULL, NULL); 
+
+	source_id = g_io_add_watch (channel,
+	                            G_IO_IN | G_IO_ERR,
+	                            (GIOFunc) inotify_event_handler,
+	                            (gpointer) self);
+	g_io_channel_unref (channel);
+	return TRUE;
+}
+
+NMInotifyHelper *
+nm_inotify_helper_get (void)
+{
+	static NMInotifyHelper *singleton = NULL;
+
+	if (!singleton) {
+		singleton = (NMInotifyHelper *) g_object_new (NM_TYPE_INOTIFY_HELPER, NULL);
+		if (!singleton)
+			return NULL;
+
+		if (!init_inotify (singleton)) {
+			g_object_unref (singleton);
+			return NULL;
+		}
+	} else
+		g_object_ref (singleton);
+
+	g_assert (singleton);
+	return singleton;
+}
+
+static void
+nm_inotify_helper_init (NMInotifyHelper *self)
+{
+	NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self);
+
+	priv->wd_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (object);
+
+	if (priv->ifd >= 0)
+		close (priv->ifd);
+
+	g_hash_table_destroy (priv->wd_refs);
+
+	G_OBJECT_CLASS (nm_inotify_helper_parent_class)->finalize (object);
+}
+
+static void
+nm_inotify_helper_class_init (NMInotifyHelperClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMInotifyHelperPrivate));
+
+	/* Virtual methods */
+	object_class->finalize = finalize;
+
+	/* Signals */
+	signals[EVENT] =
+		g_signal_new ("event",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMInotifyHelperClass, event),
+		              NULL, NULL,
+		              _nm_marshal_VOID__POINTER_STRING,
+		              G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_STRING);
+}
+
diff --git a/src/system-settings/nm-inotify-helper.h b/src/system-settings/nm-inotify-helper.h
new file mode 100644
index 00000000..10d43ac9
--- /dev/null
+++ b/src/system-settings/nm-inotify-helper.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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.
+ *
+ * (C) Copyright 2008 Red Hat, Inc.
+ */
+
+#ifndef __INOTIFY_HELPER_H__
+#define __INOTIFY_HELPER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <sys/inotify.h>
+
+#define NM_TYPE_INOTIFY_HELPER            (nm_inotify_helper_get_type ())
+#define NM_INOTIFY_HELPER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelper))
+#define NM_INOTIFY_HELPER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass))
+#define NM_IS_INOTIFY_HELPER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_INOTIFY_HELPER))
+#define NM_IS_INOTIFY_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_INOTIFY_HELPER))
+#define NM_INOTIFY_HELPER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass))
+
+typedef struct {
+	GObject parent;
+} NMInotifyHelper;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* signals */
+	void (* event) (NMInotifyHelper *helper, struct inotify_event *evt, const char *filename);
+} NMInotifyHelperClass;
+
+GType nm_inotify_helper_get_type (void);
+
+NMInotifyHelper * nm_inotify_helper_get (void);
+
+int nm_inotify_helper_add_watch (NMInotifyHelper *helper, const char *path);
+
+void nm_inotify_helper_remove_watch (NMInotifyHelper *helper, int wd);
+
+#endif  /* __INOTIFY_HELPER_H__ */
diff --git a/src/system-settings/nm-polkit-helpers.h b/src/system-settings/nm-polkit-helpers.h
new file mode 100644
index 00000000..c26fcc2c
--- /dev/null
+++ b/src/system-settings/nm-polkit-helpers.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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.
+ *
+ * (C) Copyright 2008 Novell, Inc.
+ * (C) Copyright 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_POLKIT_HELPERS_H
+#define NM_POLKIT_HELPERS_H
+
+#include <polkit/polkit.h>
+
+#define NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY    "org.freedesktop.network-manager-settings.system.modify"
+#define NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_PROTECTED "org.freedesktop.network-manager-settings.system.wifi.share.protected"
+#define NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_OPEN      "org.freedesktop.network-manager-settings.system.wifi.share.open"
+#define NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY      "org.freedesktop.network-manager-settings.system.hostname.modify"
+
+#endif /* NM_POLKIT_HELPERS_H */
diff --git a/src/system-settings/nm-sysconfig-connection.c b/src/system-settings/nm-sysconfig-connection.c
new file mode 100644
index 00000000..f46895ac
--- /dev/null
+++ b/src/system-settings/nm-sysconfig-connection.c
@@ -0,0 +1,658 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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.
+ *
+ * (C) Copyright 2008 Novell, Inc.
+ * (C) Copyright 2008 - 2009 Red Hat, Inc.
+ */
+
+#include <NetworkManager.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "nm-sysconfig-connection.h"
+#include "nm-system-config-error.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-settings-connection-interface.h"
+#include "nm-settings-interface.h"
+#include "nm-polkit-helpers.h"
+#include "nm-logging.h"
+
+
+static void settings_connection_interface_init (NMSettingsConnectionInterface *klass);
+
+G_DEFINE_TYPE_EXTENDED (NMSysconfigConnection, nm_sysconfig_connection, NM_TYPE_EXPORTED_CONNECTION, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_CONNECTION_INTERFACE,
+                                               settings_connection_interface_init))
+
+#define NM_SYSCONFIG_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                                NM_TYPE_SYSCONFIG_CONNECTION, \
+                                                NMSysconfigConnectionPrivate))
+
+typedef struct {
+	PolkitAuthority *authority;
+	GSList *pk_calls;
+	NMConnection *secrets;
+} NMSysconfigConnectionPrivate;
+
+/**************************************************************/
+
+static void
+ignore_cb (NMSettingsConnectionInterface *connection,
+           GError *error,
+           gpointer user_data)
+{
+}
+
+gboolean
+nm_sysconfig_connection_update (NMSysconfigConnection *self,
+                                NMConnection *new,
+                                gboolean signal_update,
+                                GError **error)
+{
+	NMSysconfigConnectionPrivate *priv;
+	GHashTable *new_settings;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SYSCONFIG_CONNECTION (self), FALSE);
+	g_return_val_if_fail (new != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (new), FALSE);
+
+	priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+
+	/* Do nothing if there's nothing to update */
+	if (nm_connection_compare (NM_CONNECTION (self),
+	                           NM_CONNECTION (new),
+	                           NM_SETTING_COMPARE_FLAG_EXACT))
+		return TRUE;
+
+	new_settings = nm_connection_to_hash (new);
+	g_assert (new_settings);
+	if (nm_connection_replace_settings (NM_CONNECTION (self), new_settings, error)) {
+		/* Copy the connection to keep its secrets around even if NM
+		 * calls nm_connection_clear_secrets().
+		 */
+		if (priv->secrets)
+			g_object_unref (priv->secrets);
+		priv->secrets = nm_connection_duplicate (NM_CONNECTION (self));
+
+		if (signal_update) {
+			nm_settings_connection_interface_update (NM_SETTINGS_CONNECTION_INTERFACE (self),
+			                                         ignore_cb,
+			                                         NULL);
+		}
+		success = TRUE;
+	}
+	g_hash_table_destroy (new_settings);
+	return success;
+}
+
+/**************************************************************/
+
+static GValue *
+string_to_gvalue (const char *str)
+{
+	GValue *val = g_slice_new0 (GValue);
+
+	g_value_init (val, G_TYPE_STRING);
+	g_value_set_string (val, str);
+	return val;
+}
+
+static GValue *
+byte_array_to_gvalue (const GByteArray *array)
+{
+	GValue *val;
+
+	val = g_slice_new0 (GValue);
+	g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY);
+	g_value_set_boxed (val, array);
+
+	return val;
+}
+
+static void
+copy_one_secret (gpointer key, gpointer value, gpointer user_data)
+{
+	const char *value_str = (const char *) value;
+
+	if (value_str) {
+		g_hash_table_insert ((GHashTable *) user_data,
+		                     g_strdup ((char *) key),
+		                     string_to_gvalue (value_str));
+	}
+}
+
+static void
+add_secrets (NMSetting *setting,
+             const char *key,
+             const GValue *value,
+             GParamFlags flags,
+             gpointer user_data)
+{
+	GHashTable *secrets = user_data;
+
+	if (!(flags & NM_SETTING_PARAM_SECRET))
+		return;
+
+	/* Copy secrets into the returned hash table */
+	if (G_VALUE_HOLDS_STRING (value)) {
+		const char *tmp;
+
+		tmp = g_value_get_string (value);
+		if (tmp)
+			g_hash_table_insert (secrets, g_strdup (key), string_to_gvalue (tmp));
+	} else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_MAP_OF_STRING)) {
+		/* Flatten the string hash by pulling its keys/values out */
+		g_hash_table_foreach (g_value_get_boxed (value), copy_one_secret, secrets);
+	} else if (G_VALUE_TYPE (value) == DBUS_TYPE_G_UCHAR_ARRAY) {
+		GByteArray *array;
+
+		array = g_value_get_boxed (value);
+		if (array)
+			g_hash_table_insert (secrets, g_strdup (key), byte_array_to_gvalue (array));
+	}
+}
+
+static void
+destroy_gvalue (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+static gboolean
+get_secrets (NMSettingsConnectionInterface *connection,
+	         const char *setting_name,
+             const char **hints,
+             gboolean request_new,
+             NMSettingsConnectionInterfaceGetSecretsFunc callback,
+             gpointer user_data)
+{
+	NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (connection);
+	NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+	GHashTable *settings = NULL;
+	GHashTable *secrets = NULL;
+	NMSetting *setting;
+	GError *error = NULL;
+
+	/* Use priv->secrets to work around the fact that nm_connection_clear_secrets()
+	 * will clear secrets on this object's settings.  priv->secrets should be
+	 * a complete copy of this object and kept in sync by
+	 * nm_sysconfig_connection_update().
+	 */
+	if (!priv->secrets) {
+		error = g_error_new (NM_SETTINGS_INTERFACE_ERROR,
+		                     NM_SETTINGS_INTERFACE_ERROR_INVALID_CONNECTION,
+		                     "%s.%d - Internal error; secrets cache invalid.",
+		                     __FILE__, __LINE__);
+		(*callback) (connection, NULL, error, user_data);
+		g_error_free (error);
+		return TRUE;
+	}
+
+	setting = nm_connection_get_setting_by_name (priv->secrets, setting_name);
+	if (!setting) {
+		error = g_error_new (NM_SETTINGS_INTERFACE_ERROR,
+		                     NM_SETTINGS_INTERFACE_ERROR_INVALID_SETTING,
+		                     "%s.%d - Connection didn't have requested setting '%s'.",
+		                     __FILE__, __LINE__, setting_name);
+		(*callback) (connection, NULL, error, user_data);
+		g_error_free (error);
+		return TRUE;
+	}
+
+	/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
+	 * will contain all the individual settings hashes.
+	 */
+	settings = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                  g_free, (GDestroyNotify) g_hash_table_destroy);
+
+	/* Add the secrets from this setting to the inner secrets hash for this setting */
+	secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue);
+	nm_setting_enumerate_values (setting, add_secrets, secrets);
+
+	g_hash_table_insert (settings, g_strdup (setting_name), secrets);
+	callback (connection, settings, NULL, user_data);
+	g_hash_table_destroy (settings);
+	return TRUE;
+}
+
+/**************************************************************/
+
+typedef struct {
+	NMSysconfigConnection *self;
+	DBusGMethodInvocation *context;
+	PolkitSubject *subject;
+	GCancellable *cancellable;
+	gboolean disposed;
+
+	/* Update */
+	NMConnection *connection;
+
+	/* Secrets */
+	char *setting_name;
+	char **hints;
+	gboolean request_new;
+} PolkitCall;
+
+static PolkitCall *
+polkit_call_new (NMSysconfigConnection *self,
+                 DBusGMethodInvocation *context,
+                 NMConnection *connection,
+                 const char *setting_name,
+                 const char **hints,
+                 gboolean request_new)
+{
+	PolkitCall *call;
+	char *sender;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (context != NULL, NULL);
+
+	call = g_malloc0 (sizeof (PolkitCall));
+	call->self = self;
+	call->context = context;
+	call->cancellable = g_cancellable_new ();
+	call->connection = connection;
+	call->setting_name = g_strdup (setting_name);
+	if (hints)
+		call->hints = g_strdupv ((char **) hints);
+	call->request_new = request_new;
+
+ 	sender = dbus_g_method_get_sender (context);
+	call->subject = polkit_system_bus_name_new (sender);
+	g_free (sender);
+
+	return call;
+}
+
+static void
+polkit_call_free (PolkitCall *call)
+{
+	if (call->connection)
+		g_object_unref (call->connection);
+	g_free (call->setting_name);
+	if (call->hints)
+		g_strfreev (call->hints);
+
+	g_object_unref (call->subject);
+	g_object_unref (call->cancellable);
+	g_free (call);
+}
+
+static void
+con_update_cb (NMSettingsConnectionInterface *connection,
+               GError *error,
+               gpointer user_data)
+{
+	PolkitCall *call = user_data;
+
+	if (error)
+		dbus_g_method_return_error (call->context, error);
+	else
+		dbus_g_method_return (call->context);
+
+	polkit_call_free (call);
+}
+
+static void
+pk_update_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+	PolkitCall *call = user_data;
+	NMSysconfigConnection *self = call->self;
+	NMSysconfigConnectionPrivate *priv;
+	PolkitAuthorizationResult *pk_result;
+	GError *error = NULL;
+
+	/* If our NMSysconfigConnection is already gone, do nothing */
+	if (call->disposed) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
+		                             "Request was canceled.");
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		return;
+	}
+
+	priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+
+	priv->pk_calls = g_slist_remove (priv->pk_calls, call);
+
+	pk_result = polkit_authority_check_authorization_finish (priv->authority,
+	                                                         result,
+	                                                         &error);
+	/* Some random error happened */
+	if (error) {
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		return;
+	}
+
+	/* Caller didn't successfully authenticate */
+	if (!polkit_authorization_result_get_is_authorized (pk_result)) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
+		                             "Insufficient privileges.");
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		goto out;
+	}
+
+	/* Update our settings internally so the update() call will save the new
+	 * ones.  We don't let nm_sysconfig_connection_update() handle the update
+	 * signal since we need our own callback after the update is done.
+	 */
+	if (!nm_sysconfig_connection_update (self, call->connection, FALSE, &error)) {
+		/* Shouldn't really happen since we've already validated the settings */
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		goto out;
+	}
+
+	/* Caller is authenticated, now we can finally try to commit the update */
+	nm_settings_connection_interface_update (NM_SETTINGS_CONNECTION_INTERFACE (self),
+	                                         con_update_cb,
+	                                         call);
+
+out:
+	g_object_unref (pk_result);
+}
+
+static void
+dbus_update (NMExportedConnection *exported,
+             GHashTable *new_settings,
+             DBusGMethodInvocation *context)
+{
+	NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (exported);
+	NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+	PolkitCall *call;
+	NMConnection *tmp;
+	GError *error = NULL;
+
+	/* Check if the settings are valid first */
+	tmp = nm_connection_new_from_hash (new_settings, &error);
+	if (!tmp) {
+		g_assert (error);
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+		return;
+	}
+
+	call = polkit_call_new (self, context, tmp, NULL, NULL, FALSE);
+	g_assert (call);
+	polkit_authority_check_authorization (priv->authority,
+	                                      call->subject,
+	                                      NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
+	                                      NULL,
+	                                      POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+	                                      call->cancellable,
+	                                      pk_update_cb,
+	                                      call);
+	priv->pk_calls = g_slist_prepend (priv->pk_calls, call);
+}
+
+static void
+con_delete_cb (NMSettingsConnectionInterface *connection,
+               GError *error,
+               gpointer user_data)
+{
+	PolkitCall *call = user_data;
+
+	if (error)
+		dbus_g_method_return_error (call->context, error);
+	else
+		dbus_g_method_return (call->context);
+
+	polkit_call_free (call);
+}
+
+static void
+pk_delete_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+	PolkitCall *call = user_data;
+	NMSysconfigConnection *self = call->self;
+	NMSysconfigConnectionPrivate *priv;
+	PolkitAuthorizationResult *pk_result;
+	GError *error = NULL;
+
+	/* If our NMSysconfigConnection is already gone, do nothing */
+	if (call->disposed) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
+		                             "Request was canceled.");
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		return;
+	}
+
+	priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+
+	priv->pk_calls = g_slist_remove (priv->pk_calls, call);
+
+	pk_result = polkit_authority_check_authorization_finish (priv->authority,
+	                                                         result,
+	                                                         &error);
+	/* Some random error happened */
+	if (error) {
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		return;
+	}
+
+	/* Caller didn't successfully authenticate */
+	if (!polkit_authorization_result_get_is_authorized (pk_result)) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
+		                             "Insufficient privileges.");
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		goto out;
+	}
+
+	/* Caller is authenticated, now we can finally try to delete */
+	nm_settings_connection_interface_delete (NM_SETTINGS_CONNECTION_INTERFACE (self),
+	                                         con_delete_cb,
+	                                         call);
+
+out:
+	g_object_unref (pk_result);
+}
+
+static void
+dbus_delete (NMExportedConnection *exported,
+             DBusGMethodInvocation *context)
+{
+	NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (exported);
+	NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+	PolkitCall *call;
+
+	call = polkit_call_new (self, context, NULL, NULL, NULL, FALSE);
+	g_assert (call);
+	polkit_authority_check_authorization (priv->authority,
+	                                      call->subject,
+	                                      NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
+	                                      NULL,
+	                                      POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+	                                      call->cancellable,
+	                                      pk_delete_cb,
+	                                      call);
+	priv->pk_calls = g_slist_prepend (priv->pk_calls, call);
+}
+
+static void
+con_secrets_cb (NMSettingsConnectionInterface *connection,
+                GHashTable *secrets,
+                GError *error,
+                gpointer user_data)
+{
+	PolkitCall *call = user_data;
+
+	if (error)
+		dbus_g_method_return_error (call->context, error);
+	else
+		dbus_g_method_return (call->context, secrets);
+
+	polkit_call_free (call);
+}
+
+static void
+pk_secrets_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+	PolkitCall *call = user_data;
+	NMSysconfigConnection *self = call->self;
+	NMSysconfigConnectionPrivate *priv;
+	PolkitAuthorizationResult *pk_result;
+	GError *error = NULL;
+
+	/* If our NMSysconfigConnection is already gone, do nothing */
+	if (call->disposed) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
+		                             "Request was canceled.");
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		return;
+	}
+
+	priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+
+	priv->pk_calls = g_slist_remove (priv->pk_calls, call);
+
+	pk_result = polkit_authority_check_authorization_finish (priv->authority,
+	                                                         result,
+	                                                         &error);
+	/* Some random error happened */
+	if (error) {
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		return;
+	}
+
+	/* Caller didn't successfully authenticate */
+	if (!polkit_authorization_result_get_is_authorized (pk_result)) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
+		                             "Insufficient privileges.");
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		goto out;
+	}
+
+	/* Caller is authenticated, now we can finally try to update */
+	nm_settings_connection_interface_get_secrets (NM_SETTINGS_CONNECTION_INTERFACE (self),
+	                                              call->setting_name,
+	                                              (const char **) call->hints,
+	                                              call->request_new,
+	                                              con_secrets_cb,
+	                                              call);
+
+out:
+	g_object_unref (pk_result);
+}
+
+static void
+dbus_get_secrets (NMExportedConnection *exported,
+                  const gchar *setting_name,
+                  const gchar **hints,
+                  gboolean request_new,
+                  DBusGMethodInvocation *context)
+{
+	NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (exported);
+	NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+	PolkitCall *call;
+
+	call = polkit_call_new (self, context, NULL, setting_name, hints, request_new);
+	g_assert (call);
+	polkit_authority_check_authorization (priv->authority,
+	                                      call->subject,
+	                                      NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
+	                                      NULL,
+	                                      POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+	                                      call->cancellable,
+	                                      pk_secrets_cb,
+	                                      call);
+	priv->pk_calls = g_slist_prepend (priv->pk_calls, call);
+}
+
+/**************************************************************/
+
+static void
+settings_connection_interface_init (NMSettingsConnectionInterface *iface)
+{
+	iface->get_secrets = get_secrets;
+}
+
+static void
+nm_sysconfig_connection_init (NMSysconfigConnection *self)
+{
+	NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+
+	priv->authority = polkit_authority_get ();
+	if (!priv->authority) {
+		nm_log_err (LOGD_SYS_SET, "%s: error creating PolicyKit authority");
+	}
+}
+
+static void
+dispose (GObject *object)
+{
+	NMSysconfigConnection *self = NM_SYSCONFIG_CONNECTION (object);
+	NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+	GSList *iter;
+
+	if (priv->secrets)
+		g_object_unref (priv->secrets);
+
+	/* Cancel PolicyKit requests */
+	for (iter = priv->pk_calls; iter; iter = g_slist_next (iter)) {
+		PolkitCall *call = iter->data;
+
+		call->disposed = TRUE;
+		g_cancellable_cancel (call->cancellable);
+	}
+	g_slist_free (priv->pk_calls);
+	priv->pk_calls = NULL;
+
+	G_OBJECT_CLASS (nm_sysconfig_connection_parent_class)->dispose (object);
+}
+
+static void
+nm_sysconfig_connection_class_init (NMSysconfigConnectionClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+	NMExportedConnectionClass *ec_class = NM_EXPORTED_CONNECTION_CLASS (class);
+
+	g_type_class_add_private (class, sizeof (NMSysconfigConnectionPrivate));
+
+	/* Virtual methods */
+	object_class->dispose = dispose;
+	ec_class->update = dbus_update;
+	ec_class->delete = dbus_delete;
+	ec_class->get_secrets = dbus_get_secrets;
+}
diff --git a/src/system-settings/nm-sysconfig-connection.h b/src/system-settings/nm-sysconfig-connection.h
new file mode 100644
index 00000000..0431dc95
--- /dev/null
+++ b/src/system-settings/nm-sysconfig-connection.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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.
+ *
+ * (C) Copyright 2008 Novell, Inc.
+ */
+
+#ifndef NM_SYSCONFIG_CONNECTION_H
+#define NM_SYSCONFIG_CONNECTION_H
+
+#include <nm-connection.h>
+#include <nm-exported-connection.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SYSCONFIG_CONNECTION            (nm_sysconfig_connection_get_type ())
+#define NM_SYSCONFIG_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SYSCONFIG_CONNECTION, NMSysconfigConnection))
+#define NM_SYSCONFIG_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SYSCONFIG_CONNECTION, NMSysconfigConnectionClass))
+#define NM_IS_SYSCONFIG_CONNECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SYSCONFIG_CONNECTION))
+#define NM_IS_SYSCONFIG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SYSCONFIG_CONNECTION))
+#define NM_SYSCONFIG_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SYSCONFIG_CONNECTION, NMSysconfigConnectionClass))
+
+typedef struct {
+	NMExportedConnection parent;
+} NMSysconfigConnection;
+
+typedef struct {
+	NMExportedConnectionClass parent;
+} NMSysconfigConnectionClass;
+
+GType nm_sysconfig_connection_get_type (void);
+
+/* Called by a system-settings plugin to update a connection is out of sync
+ * with it's backing storage.
+ */
+gboolean nm_sysconfig_connection_update (NMSysconfigConnection *self,
+                                         NMConnection *new_settings,
+                                         gboolean signal_update,
+                                         GError **error);
+
+G_END_DECLS
+
+#endif /* NM_SYSCONFIG_CONNECTION_H */
diff --git a/src/system-settings/nm-sysconfig-settings.c b/src/system-settings/nm-sysconfig-settings.c
new file mode 100644
index 00000000..4bcfb04b
--- /dev/null
+++ b/src/system-settings/nm-sysconfig-settings.c
@@ -0,0 +1,1520 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * Søren Sandmann <sandmann@daimi.au.dk>
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ * (C) Copyright 2008 Novell, Inc.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <gmodule.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+
+#include <NetworkManager.h>
+#include <nm-connection.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <nm-settings-system-interface.h>
+
+#include <nm-setting-8021x.h>
+#include <nm-setting-bluetooth.h>
+#include <nm-setting-cdma.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
+#include <nm-setting-olpc-mesh.h>
+#include <nm-setting-ppp.h>
+#include <nm-setting-pppoe.h>
+#include <nm-setting-serial.h>
+#include <nm-setting-vpn.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-wireless-security.h>
+
+#include "../nm-device-ethernet.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-sysconfig-settings.h"
+#include "nm-sysconfig-connection.h"
+#include "nm-polkit-helpers.h"
+#include "nm-system-config-error.h"
+#include "nm-default-wired-connection.h"
+#include "nm-logging.h"
+
+#define CONFIG_KEY_NO_AUTO_DEFAULT "no-auto-default"
+
+/* LINKER CRACKROCK */
+#define EXPORT(sym) void * __export_##sym = &sym;
+
+#include "nm-inotify-helper.h"
+EXPORT(nm_inotify_helper_get_type)
+EXPORT(nm_inotify_helper_get)
+EXPORT(nm_inotify_helper_add_watch)
+EXPORT(nm_inotify_helper_remove_watch)
+
+EXPORT(nm_sysconfig_connection_get_type)
+EXPORT(nm_sysconfig_connection_update)
+/* END LINKER CRACKROCK */
+
+static void claim_connection (NMSysconfigSettings *self,
+                              NMSettingsConnectionInterface *connection,
+                              gboolean do_export);
+
+static void impl_settings_save_hostname (NMSysconfigSettings *self,
+                                         const char *hostname,
+                                         DBusGMethodInvocation *context);
+
+static void impl_settings_get_permissions (NMSysconfigSettings *self,
+                                           DBusGMethodInvocation *context);
+
+#include "nm-settings-system-glue.h"
+
+static void unmanaged_specs_changed (NMSystemConfigInterface *config, gpointer user_data);
+
+typedef struct {
+	PolkitAuthority *authority;
+	guint auth_changed_id;
+	char *config_file;
+
+	GSList *pk_calls;
+	GSList *permissions_calls;
+
+	GSList *plugins;
+	gboolean connections_loaded;
+	GHashTable *connections;
+	GSList *unmanaged_specs;
+} NMSysconfigSettingsPrivate;
+
+static void settings_system_interface_init (NMSettingsSystemInterface *klass);
+
+G_DEFINE_TYPE_WITH_CODE (NMSysconfigSettings, nm_sysconfig_settings, NM_TYPE_SETTINGS_SERVICE,
+                         G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_SYSTEM_INTERFACE,
+                                                settings_system_interface_init))
+
+#define NM_SYSCONFIG_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SYSCONFIG_SETTINGS, NMSysconfigSettingsPrivate))
+
+enum {
+	PROPERTIES_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_0,
+	PROP_UNMANAGED_SPECS,
+
+	LAST_PROP
+};
+
+static void
+load_connections (NMSysconfigSettings *self)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GSList *iter;
+
+	if (priv->connections_loaded)
+		return;
+
+	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
+		NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
+		GSList *plugin_connections;
+		GSList *elt;
+
+		plugin_connections = nm_system_config_interface_get_connections (plugin);
+
+		// FIXME: ensure connections from plugins loaded with a lower priority
+		// get rejected when they conflict with connections from a higher
+		// priority plugin.
+
+		for (elt = plugin_connections; elt; elt = g_slist_next (elt))
+			claim_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (elt->data), TRUE);
+
+		g_slist_free (plugin_connections);
+	}
+
+	priv->connections_loaded = TRUE;
+
+	/* FIXME: Bad hack */
+	unmanaged_specs_changed (NULL, self);
+}
+
+static GSList *
+list_connections (NMSettingsService *settings)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (settings);
+	GHashTableIter iter;
+	gpointer key;
+	GSList *list = NULL;
+
+	load_connections (NM_SYSCONFIG_SETTINGS (settings));
+
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, &key, NULL))
+		list = g_slist_prepend (list, NM_EXPORTED_CONNECTION (key));
+	return g_slist_reverse (list);
+}
+
+static void
+clear_unmanaged_specs (NMSysconfigSettings *self)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	g_slist_foreach (priv->unmanaged_specs, (GFunc) g_free, NULL);
+	g_slist_free (priv->unmanaged_specs);
+	priv->unmanaged_specs = NULL;
+}
+
+static char*
+uscore_to_wincaps (const char *uscore)
+{
+	const char *p;
+	GString *str;
+	gboolean last_was_uscore;
+
+	last_was_uscore = TRUE;
+  
+	str = g_string_new (NULL);
+	p = uscore;
+	while (p && *p) {
+		if (*p == '-' || *p == '_')
+			last_was_uscore = TRUE;
+		else {
+			if (last_was_uscore) {
+				g_string_append_c (str, g_ascii_toupper (*p));
+				last_was_uscore = FALSE;
+			} else
+				g_string_append_c (str, *p);
+		}
+		++p;
+	}
+
+	return g_string_free (str, FALSE);
+}
+
+static void
+notify (GObject *object, GParamSpec *pspec)
+{
+	GValue *value;
+	GHashTable *hash;
+
+	value = g_slice_new0 (GValue);
+	hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
+
+	g_value_init (value, pspec->value_type);
+	g_object_get_property (object, pspec->name, value);
+	g_hash_table_insert (hash, uscore_to_wincaps (pspec->name), value);
+	g_signal_emit (object, signals[PROPERTIES_CHANGED], 0, hash);
+	g_hash_table_destroy (hash);
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+const GSList *
+nm_sysconfig_settings_get_unmanaged_specs (NMSysconfigSettings *self)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	load_connections (self);
+	return priv->unmanaged_specs;
+}
+
+static NMSystemConfigInterface *
+get_plugin (NMSysconfigSettings *self, guint32 capability)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GSList *iter;
+
+	g_return_val_if_fail (self != NULL, NULL);
+
+	/* Do any of the plugins support setting the hostname? */
+	for (iter = priv->plugins; iter; iter = iter->next) {
+		NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;
+
+		g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
+		if (caps & capability)
+			return NM_SYSTEM_CONFIG_INTERFACE (iter->data);
+	}
+
+	return NULL;
+}
+
+char *
+nm_sysconfig_settings_get_hostname (NMSysconfigSettings *self)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GSList *iter;
+	char *hostname = NULL;
+
+	/* Hostname returned is the hostname returned from the first plugin
+	 * that provides one.
+	 */
+	for (iter = priv->plugins; iter; iter = iter->next) {
+		NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;
+
+		g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
+		if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) {
+			g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, &hostname, NULL);
+			if (hostname && strlen (hostname))
+				return hostname;
+			g_free (hostname);
+		}
+	}
+
+	return hostname;
+}
+
+static void
+plugin_connection_added (NMSystemConfigInterface *config,
+                         NMSettingsConnectionInterface *connection,
+                         gpointer user_data)
+{
+	claim_connection (NM_SYSCONFIG_SETTINGS (user_data), connection, TRUE);
+}
+
+static gboolean
+find_unmanaged_device (NMSysconfigSettings *self, const char *needle)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GSList *iter;
+
+	for (iter = priv->unmanaged_specs; iter; iter = g_slist_next (iter)) {
+		if (!strcmp ((const char *) iter->data, needle))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static void
+unmanaged_specs_changed (NMSystemConfigInterface *config,
+                         gpointer user_data)
+{
+	NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (user_data);
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GSList *iter;
+
+	clear_unmanaged_specs (self);
+
+	/* Ask all the plugins for their unmanaged specs */
+	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
+		GSList *specs, *specs_iter;
+
+		specs = nm_system_config_interface_get_unmanaged_specs (NM_SYSTEM_CONFIG_INTERFACE (iter->data));
+		for (specs_iter = specs; specs_iter; specs_iter = specs_iter->next) {
+			if (!find_unmanaged_device (self, (const char *) specs_iter->data)) {
+				priv->unmanaged_specs = g_slist_prepend (priv->unmanaged_specs, specs_iter->data);
+			} else
+				g_free (specs_iter->data);
+		}
+
+		g_slist_free (specs);
+	}
+
+	g_object_notify (G_OBJECT (self), NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS);
+}
+
+static void
+hostname_changed (NMSystemConfigInterface *config,
+                  GParamSpec *pspec,
+                  gpointer user_data)
+{
+	g_object_notify (G_OBJECT (user_data), NM_SETTINGS_SYSTEM_INTERFACE_HOSTNAME);
+}
+
+static void
+add_plugin (NMSysconfigSettings *self, NMSystemConfigInterface *plugin)
+{
+	NMSysconfigSettingsPrivate *priv;
+	char *pname = NULL;
+	char *pinfo = NULL;
+
+	g_return_if_fail (NM_IS_SYSCONFIG_SETTINGS (self));
+	g_return_if_fail (NM_IS_SYSTEM_CONFIG_INTERFACE (plugin));
+
+	priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin));
+
+	g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED,
+	                  G_CALLBACK (plugin_connection_added), self);
+	g_signal_connect (plugin, "notify::hostname", G_CALLBACK (hostname_changed), self);
+
+	nm_system_config_interface_init (plugin, NULL);
+
+	g_object_get (G_OBJECT (plugin),
+	              NM_SYSTEM_CONFIG_INTERFACE_NAME, &pname,
+	              NM_SYSTEM_CONFIG_INTERFACE_INFO, &pinfo,
+	              NULL);
+
+	g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED,
+	                  G_CALLBACK (unmanaged_specs_changed), self);
+
+	nm_log_info (LOGD_SYS_SET, "Loaded plugin %s: %s", pname, pinfo);
+	g_free (pname);
+	g_free (pinfo);
+}
+
+static GObject *
+find_plugin (GSList *list, const char *pname)
+{
+	GSList *iter;
+	GObject *obj = NULL;
+
+	g_return_val_if_fail (pname != NULL, FALSE);
+
+	for (iter = list; iter && !obj; iter = g_slist_next (iter)) {
+		NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
+		char *list_pname = NULL;
+
+		g_object_get (G_OBJECT (plugin),
+		              NM_SYSTEM_CONFIG_INTERFACE_NAME,
+		              &list_pname,
+		              NULL);
+		if (list_pname && !strcmp (pname, list_pname))
+			obj = G_OBJECT (plugin);
+
+		g_free (list_pname);
+	}
+
+	return obj;
+}
+
+static gboolean
+load_plugins (NMSysconfigSettings *self, const char *plugins, GError **error)
+{
+	GSList *list = NULL;
+	char **plist;
+	char **iter;
+	gboolean success = TRUE;
+
+	plist = g_strsplit (plugins, ",", 0);
+	if (!plist)
+		return FALSE;
+
+	for (iter = plist; *iter; iter++) {
+		GModule *plugin;
+		char *full_name, *path;
+		const char *pname = *iter;
+		GObject *obj;
+		GObject * (*factory_func) (void);
+
+		/* ifcfg-fedora was renamed ifcfg-rh; handle old configs here */
+		if (!strcmp (pname, "ifcfg-fedora"))
+			pname = "ifcfg-rh";
+
+		obj = find_plugin (list, pname);
+		if (obj)
+			continue;
+
+		full_name = g_strdup_printf ("nm-settings-plugin-%s", pname);
+		path = g_module_build_path (PLUGINDIR, full_name);
+
+		plugin = g_module_open (path, G_MODULE_BIND_LOCAL);
+		if (!plugin) {
+			g_set_error (error, 0, 0,
+			             "Could not load plugin '%s': %s",
+			             pname, g_module_error ());
+			g_free (full_name);
+			g_free (path);
+			success = FALSE;
+			break;
+		}
+
+		g_free (full_name);
+		g_free (path);
+
+		if (!g_module_symbol (plugin, "nm_system_config_factory", (gpointer) (&factory_func))) {
+			g_set_error (error, 0, 0,
+			             "Could not find plugin '%s' factory function.",
+			             pname);
+			success = FALSE;
+			break;
+		}
+
+		obj = (*factory_func) ();
+		if (!obj || !NM_IS_SYSTEM_CONFIG_INTERFACE (obj)) {
+			g_set_error (error, 0, 0,
+			             "Plugin '%s' returned invalid system config object.",
+			             pname);
+			success = FALSE;
+			break;
+		}
+
+		g_module_make_resident (plugin);
+		g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin);
+		add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (obj));
+		list = g_slist_append (list, obj);
+	}
+
+	g_strfreev (plist);
+
+	g_slist_foreach (list, (GFunc) g_object_unref, NULL);
+	g_slist_free (list);
+
+	return success;
+}
+
+static void
+connection_removed (NMSettingsConnectionInterface *connection,
+                    gpointer user_data)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (user_data);
+
+	g_hash_table_remove (priv->connections, connection);
+}
+
+static void
+claim_connection (NMSysconfigSettings *self,
+                  NMSettingsConnectionInterface *connection,
+                  gboolean do_export)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	g_return_if_fail (NM_IS_SYSCONFIG_SETTINGS (self));
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION_INTERFACE (connection));
+
+	if (g_hash_table_lookup (priv->connections, connection))
+		/* A plugin is lying to us. */
+		return;
+
+	g_hash_table_insert (priv->connections, g_object_ref (connection), GINT_TO_POINTER (1));
+	g_signal_connect (connection,
+	                  NM_SETTINGS_CONNECTION_INTERFACE_REMOVED,
+	                  G_CALLBACK (connection_removed),
+	                  self);
+
+	if (do_export) {
+		nm_settings_service_export_connection (NM_SETTINGS_SERVICE (self), connection);
+		g_signal_emit_by_name (self, NM_SETTINGS_INTERFACE_NEW_CONNECTION, connection);
+	}
+}
+
+static void
+remove_connection (NMSysconfigSettings *self,
+                   NMSettingsConnectionInterface *connection,
+                   gboolean do_signal)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	g_return_if_fail (NM_IS_SYSCONFIG_SETTINGS (self));
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION_INTERFACE (connection));
+
+	if (g_hash_table_lookup (priv->connections, connection)) {
+		g_signal_emit_by_name (G_OBJECT (connection), NM_SETTINGS_CONNECTION_INTERFACE_REMOVED);
+		g_hash_table_remove (priv->connections, connection);
+	}
+}
+
+typedef struct {
+	NMSysconfigSettings *self;
+	DBusGMethodInvocation *context;
+	PolkitSubject *subject;
+	GCancellable *cancellable;
+	gboolean disposed;
+
+	NMConnection *connection;
+	NMSettingsAddConnectionFunc callback;
+	gpointer callback_data;
+
+	char *hostname;
+
+	NMSettingsSystemPermissions permissions;
+	guint32 permissions_calls;
+} PolkitCall;
+
+#include "nm-dbus-manager.h"
+
+static PolkitCall *
+polkit_call_new (NMSysconfigSettings *self,
+                 DBusGMethodInvocation *context,
+                 NMConnection *connection,
+                 NMSettingsAddConnectionFunc callback,
+                 gpointer callback_data,
+                 const char *hostname)
+{
+	PolkitCall *call;
+	char *sender;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (context != NULL, NULL);
+
+	call = g_malloc0 (sizeof (PolkitCall));
+	call->self = self;
+	call->cancellable = g_cancellable_new ();
+	call->context = context;
+	if (connection)
+		call->connection = g_object_ref (connection);
+	if (callback) {
+		call->callback = callback;
+		call->callback_data = callback_data;
+	}
+	if (hostname)
+		call->hostname = g_strdup (hostname);
+
+ 	sender = dbus_g_method_get_sender (context);
+	call->subject = polkit_system_bus_name_new (sender);
+	g_free (sender);
+
+	return call;
+}
+
+static void
+polkit_call_free (PolkitCall *call)
+{
+	if (call->connection)
+		g_object_unref (call->connection);
+	g_object_unref (call->cancellable);
+	g_free (call->hostname);
+	g_object_unref (call->subject);
+	g_free (call);
+}
+
+static gboolean
+add_new_connection (NMSysconfigSettings *self,
+                    NMConnection *connection,
+                    GError **error)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GError *tmp_error = NULL, *last_error = NULL;
+	GSList *iter;
+	gboolean success = FALSE;
+
+	/* Here's how it works:
+	   1) plugin writes a connection.
+	   2) plugin notices that a new connection is available for reading.
+	   3) plugin reads the new connection (the one it wrote in 1) and emits 'connection-added' signal.
+	   4) NMSysconfigSettings receives the signal and adds it to it's connection list.
+	*/
+
+	for (iter = priv->plugins; iter && !success; iter = iter->next) {
+		success = nm_system_config_interface_add_connection (NM_SYSTEM_CONFIG_INTERFACE (iter->data),
+		                                                     connection,
+		                                                     &tmp_error);
+		g_clear_error (&last_error);
+		if (!success) {
+			last_error = tmp_error;
+			tmp_error = NULL;
+		}
+	}
+
+	if (!success)
+		*error = last_error;
+	return success;
+}
+
+static void
+pk_add_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+	PolkitCall *call = user_data;
+	NMSysconfigSettings *self = call->self;
+	NMSysconfigSettingsPrivate *priv;
+	PolkitAuthorizationResult *pk_result;
+	GError *error = NULL, *add_error = NULL;
+
+	/* If NMSysconfigSettings is already gone, do nothing */
+	if (call->disposed) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
+		                             "Request was canceled.");
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		return;
+	}
+
+	priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	priv->pk_calls = g_slist_remove (priv->pk_calls, call);
+
+	pk_result = polkit_authority_check_authorization_finish (priv->authority,
+	                                                         result,
+	                                                         &error);
+	/* Some random error happened */
+	if (error) {
+		call->callback (NM_SETTINGS_INTERFACE (self), error, call->callback_data);
+		goto out;
+	}
+
+	/* Caller didn't successfully authenticate */
+	if (!polkit_authorization_result_get_is_authorized (pk_result)) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
+		                             "Insufficient privileges.");
+		call->callback (NM_SETTINGS_INTERFACE (self), error, call->callback_data);
+		goto out;
+	}
+
+	if (add_new_connection (self, call->connection, &add_error))
+		call->callback (NM_SETTINGS_INTERFACE (self), NULL, call->callback_data);
+	else {
+		error = g_error_new (NM_SYSCONFIG_SETTINGS_ERROR,
+		                     NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED,
+		                     "Saving connection failed: (%d) %s",
+		                     add_error ? add_error->code : -1,
+		                     (add_error && add_error->message) ? add_error->message : "(unknown)");
+		g_error_free (add_error);
+		call->callback (NM_SETTINGS_INTERFACE (self), error, call->callback_data);
+	}
+
+out:
+	g_clear_error (&error);
+	polkit_call_free (call);
+	if (pk_result)
+		g_object_unref (pk_result);
+}
+
+static void
+add_connection (NMSettingsService *service,
+	            NMConnection *connection,
+	            DBusGMethodInvocation *context, /* Only present for D-Bus calls */
+	            NMSettingsAddConnectionFunc callback,
+	            gpointer user_data)
+{
+	NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (service);
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	PolkitCall *call;
+	GError *error = NULL;
+
+	/* Do any of the plugins support adding? */
+	if (!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_ADD_NOT_SUPPORTED,
+		                             "None of the registered plugins support add.");
+		callback (NM_SETTINGS_INTERFACE (service), error, user_data);
+		g_error_free (error);
+		return;
+	}
+
+	call = polkit_call_new (self, context, connection, callback, user_data, NULL);
+	g_assert (call);
+	polkit_authority_check_authorization (priv->authority,
+	                                      call->subject,
+	                                      NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
+	                                      NULL,
+	                                      POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+	                                      call->cancellable,
+	                                      pk_add_cb,
+	                                      call);
+	priv->pk_calls = g_slist_append (priv->pk_calls, call);
+}
+
+static void
+pk_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+	PolkitCall *call = user_data;
+	NMSysconfigSettings *self = call->self;
+	NMSysconfigSettingsPrivate *priv;
+	PolkitAuthorizationResult *pk_result;
+	GError *error = NULL;
+	GSList *iter;
+	gboolean success = FALSE;
+
+	/* If our NMSysconfigConnection is already gone, do nothing */
+	if (call->disposed) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
+		                             "Request was canceled.");
+		dbus_g_method_return_error (call->context, error);
+		g_error_free (error);
+		polkit_call_free (call);
+		return;
+	}
+
+	priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	priv->pk_calls = g_slist_remove (priv->pk_calls, call);
+
+	pk_result = polkit_authority_check_authorization_finish (priv->authority,
+	                                                         result,
+	                                                         &error);
+	/* Some random error happened */
+	if (error) {
+		dbus_g_method_return_error (call->context, error);
+		goto out;
+	}
+
+	/* Caller didn't successfully authenticate */
+	if (!polkit_authorization_result_get_is_authorized (pk_result)) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
+		                             "Insufficient privileges.");
+		dbus_g_method_return_error (call->context, error);
+		goto out;
+	}
+
+	/* Set the hostname in all plugins */
+	for (iter = priv->plugins; iter; iter = iter->next) {
+		NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;
+
+		g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
+		if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) {
+			g_object_set (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, call->hostname, NULL);
+			success = TRUE;
+		}
+	}
+
+	if (success) {
+		dbus_g_method_return (call->context);
+	} else {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED,
+		                             "Saving the hostname failed.");
+		dbus_g_method_return_error (call->context, error);
+	}
+
+out:
+	g_clear_error (&error);
+	polkit_call_free (call);
+	if (pk_result)
+		g_object_unref (pk_result);
+}
+
+static void
+impl_settings_save_hostname (NMSysconfigSettings *self,
+                             const char *hostname,
+                             DBusGMethodInvocation *context)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	PolkitCall *call;
+	GError *error = NULL;
+
+	/* Do any of the plugins support setting the hostname? */
+	if (!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) {
+		error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+		                             NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED,
+		                             "None of the registered plugins support setting the hostname.");
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+		return;
+	}
+
+	call = polkit_call_new (self, context, NULL, NULL, NULL, hostname);
+	g_assert (call);
+	polkit_authority_check_authorization (priv->authority,
+	                                      call->subject,
+	                                      NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY,
+	                                      NULL,
+	                                      POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+	                                      call->cancellable,
+	                                      pk_hostname_cb,
+	                                      call);
+	priv->pk_calls = g_slist_append (priv->pk_calls, call);
+}
+
+static void
+pk_authority_changed_cb (GObject *object, gpointer user_data)
+{
+	/* Let clients know they should re-check their authorization */
+	g_signal_emit_by_name (NM_SYSCONFIG_SETTINGS (user_data),
+                           NM_SETTINGS_SYSTEM_INTERFACE_CHECK_PERMISSIONS);
+}
+
+typedef struct {
+	PolkitCall *pk_call;
+	const char *pk_action;
+	GCancellable *cancellable;
+	NMSettingsSystemPermissions permission;
+	gboolean disposed;
+} PermissionsCall;
+
+static void
+permission_call_done (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+	PermissionsCall *call = user_data;
+	PolkitCall *pk_call = call->pk_call;
+	NMSysconfigSettings *self = pk_call->self;
+	NMSysconfigSettingsPrivate *priv;
+	PolkitAuthorizationResult *pk_result;
+	GError *error = NULL;
+
+	/* If NMSysconfigSettings is gone, just skip to the end */
+	if (call->disposed)
+		goto done;
+
+	priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	priv->permissions_calls = g_slist_remove (priv->permissions_calls, call);
+
+	pk_result = polkit_authority_check_authorization_finish (priv->authority,
+	                                                         result,
+	                                                         &error);
+	/* Some random error happened */
+	if (error) {
+		nm_log_err (LOGD_SYS_SET, "error checking '%s' permission: (%d) %s",
+		            __FILE__, __LINE__, __func__,
+		            call->pk_action,
+		            error ? error->code : -1,
+		            error && error->message ? error->message : "(unknown)");
+		if (error)
+			g_error_free (error);
+	} else {
+		/* If the caller is authorized, or the caller could authorize via a
+		 * challenge, then authorization is possible.  Otherwise, caller is out of
+		 * luck.
+		 */
+		if (   polkit_authorization_result_get_is_authorized (pk_result)
+		    || polkit_authorization_result_get_is_challenge (pk_result))
+		    pk_call->permissions |= call->permission;
+	}
+
+	g_object_unref (pk_result);
+
+done:
+	pk_call->permissions_calls--;
+	if (pk_call->permissions_calls == 0) {
+		if (call->disposed) {
+			error = g_error_new_literal (NM_SYSCONFIG_SETTINGS_ERROR,
+			                             NM_SYSCONFIG_SETTINGS_ERROR_GENERAL,
+			                             "Request was canceled.");
+			dbus_g_method_return_error (pk_call->context, error);
+			g_error_free (error);
+		} else {
+			/* All the permissions calls are done, return the full permissions
+			 * bitfield back to the user.
+			 */
+			dbus_g_method_return (pk_call->context, pk_call->permissions);
+		}
+
+		polkit_call_free (pk_call);
+	}
+	memset (call, 0, sizeof (PermissionsCall));
+	g_free (call);
+}
+
+static void
+start_permission_check (NMSysconfigSettings *self,
+                        PolkitCall *pk_call,
+                        const char *pk_action,
+                        NMSettingsSystemPermissions permission)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	PermissionsCall *call;
+
+	g_return_if_fail (pk_call != NULL);
+	g_return_if_fail (pk_action != NULL);
+	g_return_if_fail (permission != NM_SETTINGS_SYSTEM_PERMISSION_NONE);
+
+	call = g_malloc0 (sizeof (PermissionsCall));
+	call->pk_call = pk_call;
+	call->pk_action = pk_action;
+	call->permission = permission;
+	call->cancellable = g_cancellable_new ();
+
+	pk_call->permissions_calls++;
+
+	polkit_authority_check_authorization (priv->authority,
+	                                      pk_call->subject,
+	                                      pk_action,
+	                                      NULL,
+	                                      0,
+	                                      call->cancellable,
+	                                      permission_call_done,
+	                                      call);
+	priv->permissions_calls = g_slist_append (priv->permissions_calls, call);
+}
+
+static void
+impl_settings_get_permissions (NMSysconfigSettings *self,
+                               DBusGMethodInvocation *context)
+{
+	PolkitCall *call;
+
+	call = polkit_call_new (self, context, NULL, NULL, NULL, FALSE);
+	g_assert (call);
+
+	/* Start checks for the various permissions */
+
+	/* Only check for connection-modify if one of our plugins supports it. */
+	if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) {
+		start_permission_check (self, call,
+		                        NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY,
+		                        NM_SETTINGS_SYSTEM_PERMISSION_CONNECTION_MODIFY);
+	}
+
+	/* Only check for hostname-modify if one of our plugins supports it. */
+	if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) {
+		start_permission_check (self, call,
+		                        NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY,
+		                        NM_SETTINGS_SYSTEM_PERMISSION_HOSTNAME_MODIFY);
+	}
+
+	// FIXME: hook these into plugin permissions like the modify permissions */
+	start_permission_check (self, call,
+	                        NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_OPEN,
+	                        NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_OPEN);
+	start_permission_check (self, call,
+	                        NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_PROTECTED,
+	                        NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_PROTECTED);
+}
+
+static gboolean
+get_permissions (NMSettingsSystemInterface *settings,
+                 NMSettingsSystemGetPermissionsFunc callback,
+                 gpointer user_data)
+{
+	NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (settings);
+	NMSettingsSystemPermissions permissions = NM_SETTINGS_SYSTEM_PERMISSION_NONE;
+
+	/* Local caller (ie, NM) gets full permissions by default because it doesn't
+	 * need authorization.  However, permissions are still subject to plugin's
+	 * restrictions.  i.e. if no plugins support connection-modify, then even
+	 * the local caller won't get that permission.
+	 */
+
+	/* Only check for connection-modify if one of our plugins supports it. */
+	if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS))
+		permissions |= NM_SETTINGS_SYSTEM_PERMISSION_CONNECTION_MODIFY;
+
+	/* Only check for hostname-modify if one of our plugins supports it. */
+	if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME))
+		permissions |= NM_SETTINGS_SYSTEM_PERMISSION_HOSTNAME_MODIFY;
+
+	// FIXME: hook these into plugin permissions like the modify permissions */
+	permissions |= NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_OPEN;
+	permissions |= NM_SETTINGS_SYSTEM_PERMISSION_WIFI_SHARE_PROTECTED;
+
+	callback (settings, permissions, NULL, user_data);
+	return TRUE;
+}
+
+static gboolean
+have_connection_for_device (NMSysconfigSettings *self, GByteArray *mac)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GHashTableIter iter;
+	gpointer key;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	const GByteArray *setting_mac;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail (NM_IS_SYSCONFIG_SETTINGS (self), FALSE);
+	g_return_val_if_fail (mac != NULL, FALSE);
+
+	/* Find a wired connection locked to the given MAC address, if any */
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, &key, NULL)) {
+		NMConnection *connection = NM_CONNECTION (key);
+		const char *connection_type;
+
+		s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+		connection_type = nm_setting_connection_get_connection_type (s_con);
+
+		if (   strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)
+		    && strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME))
+			continue;
+
+		s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+
+		/* No wired setting; therefore the PPPoE connection applies to any device */
+		if (!s_wired && !strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) {
+			ret = TRUE;
+			break;
+		}
+
+		setting_mac = nm_setting_wired_get_mac_address (s_wired);
+		if (setting_mac) {
+			/* A connection mac-locked to this device */
+			if (!memcmp (setting_mac->data, mac->data, ETH_ALEN)) {
+				ret = TRUE;
+				break;
+			}
+		} else {
+			/* A connection that applies to any wired device */
+			ret = TRUE;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/* Search through the list of blacklisted MAC addresses in the config file. */
+static gboolean
+is_mac_auto_wired_blacklisted (NMSysconfigSettings *self, const GByteArray *mac)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GKeyFile *config;
+	char **list, **iter;
+	gboolean found = FALSE;
+
+	g_return_val_if_fail (mac != NULL, FALSE);
+
+	if (!priv->config_file)
+		return FALSE;
+
+	config = g_key_file_new ();
+	if (!config) {
+		nm_log_warn (LOGD_SYS_SET, "not enough memory to load config file.");
+		return FALSE;
+	}
+
+	g_key_file_set_list_separator (config, ',');
+	if (!g_key_file_load_from_file (config, priv->config_file, G_KEY_FILE_NONE, NULL))
+		goto out;
+
+	list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, NULL, NULL);
+	for (iter = list; iter && *iter; iter++) {
+		struct ether_addr *candidate;
+
+		candidate = ether_aton (*iter);
+		if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN)) {
+			found = TRUE;
+			break;
+		}
+	}
+
+	if (list)
+		g_strfreev (list);
+
+out:
+	g_key_file_free (config);
+	return found;
+}
+
+#define DEFAULT_WIRED_TAG "default-wired"
+
+static void
+default_wired_deleted (NMDefaultWiredConnection *wired,
+                       const GByteArray *mac,
+                       NMSysconfigSettings *self)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	NMSettingConnection *s_con;
+	char *tmp;
+	GKeyFile *config;
+	char **list, **iter, **updated;
+	gboolean found = FALSE;
+	gsize len = 0, i;
+	char *data;
+
+	/* If there was no config file specified, there's nothing to do */
+	if (!priv->config_file)
+		goto cleanup;
+
+	/* When the default wired connection is removed (either deleted or saved
+	 * to a new persistent connection by a plugin), write the MAC address of
+	 * the wired device to the config file and don't create a new default wired
+	 * connection for that device again.
+	 */
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (wired),
+	                                                           NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
+
+	/* Ignore removals of read-only connections, since they couldn't have
+	 * been removed by the user.
+	 */
+	if (nm_setting_connection_get_read_only (s_con))
+		goto cleanup;
+
+	config = g_key_file_new ();
+	if (!config)
+		goto cleanup;
+
+	g_key_file_set_list_separator (config, ',');
+	g_key_file_load_from_file (config, priv->config_file, G_KEY_FILE_KEEP_COMMENTS, NULL);
+
+	list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, &len, NULL);
+	/* Traverse entire list to get count of # items */
+	for (iter = list; iter && *iter; iter++) {
+		struct ether_addr *candidate;
+
+		candidate = ether_aton (*iter);
+		if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN))
+			found = TRUE;
+	}
+
+	/* Add this device's MAC to the list */
+	if (!found) {
+		tmp = g_strdup_printf ("%02x:%02x:%02x:%02x:%02x:%02x",
+		                       mac->data[0], mac->data[1], mac->data[2],
+		                       mac->data[3], mac->data[4], mac->data[5]);
+
+		/* New list; size + 1 for the new element, + 1 again for ending NULL */
+		updated = g_malloc0 (sizeof (char*) * (len + 2));
+
+		/* Copy original list and add new MAC */
+		for (i = 0; list && list[i]; i++)
+			updated[i] = list[i];
+		updated[i++] = tmp;
+		updated[i] = NULL;
+
+		g_key_file_set_string_list (config,
+		                            "main", CONFIG_KEY_NO_AUTO_DEFAULT,
+		                            (const char **) updated,
+		                            len + 2);
+		/* g_free() not g_strfreev() since 'updated' isn't a deep-copy */
+		g_free (updated);
+		g_free (tmp);
+
+		data = g_key_file_to_data (config, &len, NULL);
+		if (data) {
+			g_file_set_contents (priv->config_file, data, len, NULL);
+			g_free (data);
+		}
+	}
+
+	if (list)
+		g_strfreev (list);
+	g_key_file_free (config);
+
+cleanup:
+	g_object_set_data (G_OBJECT (nm_default_wired_connection_get_device (wired)),
+	                   DEFAULT_WIRED_TAG,
+	                   NULL);
+}
+
+static void
+delete_cb (NMSettingsConnectionInterface *connection, GError *error, gpointer user_data)
+{
+}
+
+static gboolean
+default_wired_try_update (NMDefaultWiredConnection *wired,
+                          NMSysconfigSettings *self)
+{
+	GError *error = NULL;
+	NMSettingConnection *s_con;
+	const char *id;
+
+	/* Try to move this default wired conneciton to a plugin so that it has
+	 * persistent storage.
+	 */
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (wired),
+	                                                           NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
+	id = nm_setting_connection_get_id (s_con);
+	g_assert (id);
+
+	remove_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (wired), FALSE);
+	if (add_new_connection (self, NM_CONNECTION (wired), &error)) {
+		nm_settings_connection_interface_delete (NM_SETTINGS_CONNECTION_INTERFACE (wired),
+		                                         delete_cb,
+		                                         NULL);
+
+		g_object_set_data (G_OBJECT (nm_default_wired_connection_get_device (wired)),
+		                   DEFAULT_WIRED_TAG,
+		                   NULL);
+		nm_log_info (LOGD_SYS_SET, "Saved default wired connection '%s' to persistent storage", id);
+		return FALSE;
+	}
+
+	nm_log_warn (LOGD_SYS_SET, "couldn't save default wired connection '%s': %d / %s",
+	             id,
+	             error ? error->code : -1,
+	             (error && error->message) ? error->message : "(unknown)");
+
+	/* If there was an error, don't destroy the default wired connection,
+	 * but add it back to the system settings service. Connection is already
+	 * exported on the bus, don't export it again, thus do_export == FALSE.
+	 */
+	claim_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (wired), FALSE);
+	return TRUE;
+}
+
+void
+nm_sysconfig_settings_device_added (NMSysconfigSettings *self, NMDevice *device)
+{
+	GByteArray *mac = NULL;
+	struct ether_addr tmp;
+	NMDefaultWiredConnection *wired;
+	NMSettingConnection *s_con;
+	gboolean read_only = TRUE;
+	const char *id;
+
+	if (nm_device_get_device_type (device) != NM_DEVICE_TYPE_ETHERNET)
+		return;
+
+	/* If the device isn't managed or it already has a default wired connection,
+	 * ignore it.
+	 */
+	if (   !nm_device_get_managed (device)
+	    || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_TAG))
+		return;
+
+	nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (device), &tmp);
+
+	mac = g_byte_array_sized_new (ETH_ALEN);
+	g_byte_array_append (mac, tmp.ether_addr_octet, ETH_ALEN);
+
+	if (   have_connection_for_device (self, mac)
+	    || is_mac_auto_wired_blacklisted (self, mac))
+		goto ignore;
+
+	if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS))
+		read_only = FALSE;
+
+	wired = nm_default_wired_connection_new (mac, device, read_only);
+	if (!wired)
+		goto ignore;
+
+	s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (wired),
+	                                                           NM_TYPE_SETTING_CONNECTION);
+	g_assert (s_con);
+	id = nm_setting_connection_get_id (s_con);
+	g_assert (id);
+
+	nm_log_info (LOGD_SYS_SET, "Added default wired connection '%s' for %s",
+	             id, nm_device_get_udi (device));
+
+	g_signal_connect (wired, "try-update", (GCallback) default_wired_try_update, self);
+	g_signal_connect (wired, "deleted", (GCallback) default_wired_deleted, self);
+	claim_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (wired), TRUE);
+	g_object_unref (wired);
+
+	g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_TAG, wired);
+
+ignore:
+	g_byte_array_free (mac, TRUE);
+}
+
+void
+nm_sysconfig_settings_device_removed (NMSysconfigSettings *self, NMDevice *device)
+{
+	NMDefaultWiredConnection *connection;
+
+	if (nm_device_get_device_type (device) != NM_DEVICE_TYPE_ETHERNET)
+		return;
+
+	connection = (NMDefaultWiredConnection *) g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_TAG);
+	if (connection)
+		remove_connection (self, NM_SETTINGS_CONNECTION_INTERFACE (connection), TRUE);
+}
+
+NMSysconfigSettings *
+nm_sysconfig_settings_new (const char *config_file,
+                           const char *plugins,
+                           DBusGConnection *bus,
+                           GError **error)
+{
+	NMSysconfigSettings *self;
+	NMSysconfigSettingsPrivate *priv;
+
+	self = g_object_new (NM_TYPE_SYSCONFIG_SETTINGS,
+	                     NM_SETTINGS_SERVICE_BUS, bus,
+	                     NM_SETTINGS_SERVICE_SCOPE, NM_CONNECTION_SCOPE_SYSTEM,
+	                     NULL);
+	if (!self)
+		return NULL;
+
+	priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	priv->config_file = g_strdup (config_file);
+
+	if (plugins) {
+		/* Load the plugins; fail if a plugin is not found. */
+		if (!load_plugins (self, plugins, error)) {
+			g_object_unref (self);
+			return NULL;
+		}
+		unmanaged_specs_changed (NULL, self);
+	}
+
+	return self;
+}
+
+/***************************************************************/
+
+static void
+dispose (GObject *object)
+{
+	NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (object);
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+	GSList *iter;
+
+	if (priv->auth_changed_id) {
+		g_signal_handler_disconnect (priv->authority, priv->auth_changed_id);
+		priv->auth_changed_id = 0;
+	}
+
+	/* Cancel PolicyKit requests */
+	for (iter = priv->pk_calls; iter; iter = g_slist_next (iter)) {
+		PolkitCall *call = iter->data;
+
+		call->disposed = TRUE;
+		g_cancellable_cancel (call->cancellable);
+	}
+	g_slist_free (priv->pk_calls);
+	priv->pk_calls = NULL;
+
+	/* Cancel PolicyKit permissions requests */
+	for (iter = priv->permissions_calls; iter; iter = g_slist_next (iter)) {
+		PermissionsCall *call = iter->data;
+
+		call->disposed = TRUE;
+		g_cancellable_cancel (call->cancellable);
+	}
+	g_slist_free (priv->permissions_calls);
+	priv->permissions_calls = NULL;
+
+	G_OBJECT_CLASS (nm_sysconfig_settings_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (object);
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	g_hash_table_destroy (priv->connections);
+
+	clear_unmanaged_specs (self);
+
+	g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL);
+	g_slist_free (priv->plugins);
+
+	g_free (priv->config_file);
+
+	G_OBJECT_CLASS (nm_sysconfig_settings_parent_class)->finalize (object);
+}
+
+static void
+settings_system_interface_init (NMSettingsSystemInterface *iface)
+{
+	iface->get_permissions = get_permissions;
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_INTERFACE (iface),
+	                                 &dbus_glib_nm_settings_system_object_info);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMSysconfigSettings *self = NM_SYSCONFIG_SETTINGS (object);
+	const GSList *specs, *iter;
+	GSList *copy = NULL;
+
+	switch (prop_id) {
+	case PROP_UNMANAGED_SPECS:
+		specs = nm_sysconfig_settings_get_unmanaged_specs (self);
+		for (iter = specs; iter; iter = g_slist_next (iter))
+			copy = g_slist_append (copy, g_strdup (iter->data));
+		g_value_take_boxed (value, copy);
+		break;
+	case NM_SETTINGS_SYSTEM_INTERFACE_PROP_HOSTNAME:
+		g_value_take_string (value, nm_sysconfig_settings_get_hostname (self));
+
+		/* Don't ever pass NULL through D-Bus */
+		if (!g_value_get_string (value))
+			g_value_set_static_string (value, "");
+		break;
+	case NM_SETTINGS_SYSTEM_INTERFACE_PROP_CAN_MODIFY:
+		g_value_set_boolean (value, !!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_sysconfig_settings_class_init (NMSysconfigSettingsClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+	NMSettingsServiceClass *ss_class = NM_SETTINGS_SERVICE_CLASS (class);
+	
+	g_type_class_add_private (class, sizeof (NMSysconfigSettingsPrivate));
+
+	/* virtual methods */
+	object_class->notify = notify;
+	object_class->get_property = get_property;
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+	ss_class->list_connections = list_connections;
+	ss_class->add_connection = add_connection;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_UNMANAGED_SPECS,
+		 g_param_spec_boxed (NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS,
+							 "Unamanged device specs",
+							 "Unmanaged device specs",
+							 DBUS_TYPE_G_LIST_OF_STRING,
+							 G_PARAM_READABLE));
+
+	g_object_class_override_property (object_class,
+									  NM_SETTINGS_SYSTEM_INTERFACE_PROP_HOSTNAME,
+									  NM_SETTINGS_SYSTEM_INTERFACE_HOSTNAME);
+
+	g_object_class_override_property (object_class,
+									  NM_SETTINGS_SYSTEM_INTERFACE_PROP_CAN_MODIFY,
+									  NM_SETTINGS_SYSTEM_INTERFACE_CAN_MODIFY);
+
+	/* signals */
+	signals[PROPERTIES_CHANGED] = 
+	                g_signal_new ("properties-changed",
+	                              G_OBJECT_CLASS_TYPE (object_class),
+	                              G_SIGNAL_RUN_FIRST,
+	                              G_STRUCT_OFFSET (NMSysconfigSettingsClass, properties_changed),
+	                              NULL, NULL,
+	                              g_cclosure_marshal_VOID__BOXED,
+	                              G_TYPE_NONE, 1, DBUS_TYPE_G_MAP_OF_VARIANT);
+
+	dbus_g_error_domain_register (NM_SYSCONFIG_SETTINGS_ERROR,
+	                              NM_DBUS_IFACE_SETTINGS_SYSTEM,
+	                              NM_TYPE_SYSCONFIG_SETTINGS_ERROR);
+
+	dbus_g_error_domain_register (NM_SETTINGS_INTERFACE_ERROR,
+	                              NM_DBUS_IFACE_SETTINGS,
+	                              NM_TYPE_SETTINGS_INTERFACE_ERROR);
+
+	/* And register all the settings errors with D-Bus */
+	dbus_g_error_domain_register (NM_CONNECTION_ERROR, NULL, NM_TYPE_CONNECTION_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_802_1X_ERROR, NULL, NM_TYPE_SETTING_802_1X_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_BLUETOOTH_ERROR, NULL, NM_TYPE_SETTING_BLUETOOTH_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_CDMA_ERROR, NULL, NM_TYPE_SETTING_CDMA_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_CONNECTION_ERROR, NULL, NM_TYPE_SETTING_CONNECTION_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_GSM_ERROR, NULL, NM_TYPE_SETTING_GSM_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_IP4_CONFIG_ERROR, NULL, NM_TYPE_SETTING_IP4_CONFIG_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_IP6_CONFIG_ERROR, NULL, NM_TYPE_SETTING_IP6_CONFIG_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_OLPC_MESH_ERROR, NULL, NM_TYPE_SETTING_OLPC_MESH_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_PPP_ERROR, NULL, NM_TYPE_SETTING_PPP_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_PPPOE_ERROR, NULL, NM_TYPE_SETTING_PPPOE_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_SERIAL_ERROR, NULL, NM_TYPE_SETTING_SERIAL_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_VPN_ERROR, NULL, NM_TYPE_SETTING_VPN_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_WIRED_ERROR, NULL, NM_TYPE_SETTING_WIRED_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_WIRELESS_SECURITY_ERROR, NULL, NM_TYPE_SETTING_WIRELESS_SECURITY_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_WIRELESS_ERROR, NULL, NM_TYPE_SETTING_WIRELESS_ERROR);
+	dbus_g_error_domain_register (NM_SETTING_ERROR, NULL, NM_TYPE_SETTING_ERROR);
+}
+
+static void
+nm_sysconfig_settings_init (NMSysconfigSettings *self)
+{
+	NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+
+	priv->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
+
+	priv->authority = polkit_authority_get ();
+	if (priv->authority) {
+		priv->auth_changed_id = g_signal_connect (priv->authority,
+		                                          "changed",
+		                                          G_CALLBACK (pk_authority_changed_cb),
+		                                          self);
+	} else
+		nm_log_warn (LOGD_SYS_SET, "failed to create PolicyKit authority.");
+}
+
diff --git a/src/system-settings/nm-sysconfig-settings.h b/src/system-settings/nm-sysconfig-settings.h
new file mode 100644
index 00000000..ae2ba683
--- /dev/null
+++ b/src/system-settings/nm-sysconfig-settings.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * Søren Sandmann <sandmann@daimi.au.dk>
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
+ * (C) Copyright 2008 Novell, Inc.
+ */
+
+#ifndef __NM_SYSCONFIG_SETTINGS_H__
+#define __NM_SYSCONFIG_SETTINGS_H__
+
+#include <nm-connection.h>
+#include <nm-settings-service.h>
+
+#include "nm-sysconfig-connection.h"
+#include "nm-system-config-interface.h"
+#include "nm-device.h"
+
+#define NM_TYPE_SYSCONFIG_SETTINGS            (nm_sysconfig_settings_get_type ())
+#define NM_SYSCONFIG_SETTINGS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SYSCONFIG_SETTINGS, NMSysconfigSettings))
+#define NM_SYSCONFIG_SETTINGS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_SYSCONFIG_SETTINGS, NMSysconfigSettingsClass))
+#define NM_IS_SYSCONFIG_SETTINGS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SYSCONFIG_SETTINGS))
+#define NM_IS_SYSCONFIG_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_SYSCONFIG_SETTINGS))
+#define NM_SYSCONFIG_SETTINGS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_SYSCONFIG_SETTINGS, NMSysconfigSettingsClass))
+
+#define NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS "unmanaged-specs"
+
+typedef struct {
+	NMSettingsService parent_instance;
+} NMSysconfigSettings;
+
+typedef struct {
+	NMSettingsServiceClass parent_class;
+
+	/* Signals */
+	void (*properties_changed) (NMSysconfigSettings *self, GHashTable *properties);
+} NMSysconfigSettingsClass;
+
+GType nm_sysconfig_settings_get_type (void);
+
+NMSysconfigSettings *nm_sysconfig_settings_new (const char *config_file,
+                                                const char *plugins,
+                                                DBusGConnection *bus,
+                                                GError **error);
+
+const GSList *nm_sysconfig_settings_get_unmanaged_specs (NMSysconfigSettings *self);
+
+char *nm_sysconfig_settings_get_hostname (NMSysconfigSettings *self);
+
+void nm_sysconfig_settings_device_added (NMSysconfigSettings *self, NMDevice *device);
+
+void nm_sysconfig_settings_device_removed (NMSysconfigSettings *self, NMDevice *device);
+
+#endif  /* __NM_SYSCONFIG_SETTINGS_H__ */
diff --git a/src/system-settings/nm-system-config-error.c b/src/system-settings/nm-system-config-error.c
new file mode 100644
index 00000000..13d47462
--- /dev/null
+++ b/src/system-settings/nm-system-config-error.c
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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) 2008 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#include "nm-system-config-error.h"
+
+GQuark
+nm_sysconfig_settings_error_quark (void)
+{
+	static GQuark ret = 0;
+
+	if (ret == 0)
+		ret = g_quark_from_static_string ("nm_sysconfig_settings_error");
+
+	return ret;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_sysconfig_settings_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_GENERAL, "GeneralError"),
+			ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED, "NotPrivileged"),
+			ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_ADD_NOT_SUPPORTED, "AddNotSupported"),
+			ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_UPDATE_NOT_SUPPORTED, "UpdateNotSupported"),
+			ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_DELETE_NOT_SUPPORTED, "DeleteNotSupported"),
+			ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED, "AddFailed"),
+			ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED, "SaveHostnameNotSupported"),
+			ENUM_ENTRY (NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED, "SaveHostnameFailed"),
+			{ 0, 0, 0 }
+		};
+
+		etype = g_enum_register_static ("NMSysconfigSettingsError", values);
+	}
+
+	return etype;
+}
diff --git a/src/system-settings/nm-system-config-error.h b/src/system-settings/nm-system-config-error.h
new file mode 100644
index 00000000..63896fcd
--- /dev/null
+++ b/src/system-settings/nm-system-config-error.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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) 2008 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_SYSTEM_CONFIG_ERROR_H
+#define NM_SYSTEM_CONFIG_ERROR_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+enum {
+	NM_SYSCONFIG_SETTINGS_ERROR_GENERAL = 0,
+	NM_SYSCONFIG_SETTINGS_ERROR_NOT_PRIVILEGED,
+	NM_SYSCONFIG_SETTINGS_ERROR_ADD_NOT_SUPPORTED,
+	NM_SYSCONFIG_SETTINGS_ERROR_UPDATE_NOT_SUPPORTED,
+	NM_SYSCONFIG_SETTINGS_ERROR_DELETE_NOT_SUPPORTED,
+	NM_SYSCONFIG_SETTINGS_ERROR_ADD_FAILED,
+	NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED,
+	NM_SYSCONFIG_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED,
+};
+
+#define NM_SYSCONFIG_SETTINGS_ERROR (nm_sysconfig_settings_error_quark ())
+#define NM_TYPE_SYSCONFIG_SETTINGS_ERROR (nm_sysconfig_settings_error_get_type ())
+
+GQuark nm_sysconfig_settings_error_quark    (void);
+GType  nm_sysconfig_settings_error_get_type (void);
+
+#endif /* NM_SYSTEM_CONFIG_ERROR_H */
diff --git a/src/system-settings/nm-system-config-interface.c b/src/system-settings/nm-system-config-interface.c
new file mode 100644
index 00000000..90fd93ba
--- /dev/null
+++ b/src/system-settings/nm-system-config-interface.c
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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) 2007 - 2008 Red Hat, Inc.
+ * Copyright (C) 2008 Novell, Inc.
+ */
+
+#include "nm-system-config-interface.h"
+
+static void
+interface_init (gpointer g_iface)
+{
+	GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+	static gboolean initialized = FALSE;
+
+	if (initialized)
+		return;
+
+	/* Properties */
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_string (NM_SYSTEM_CONFIG_INTERFACE_NAME,
+							  "Name",
+							  "Plugin name",
+							  NULL,
+							  G_PARAM_READABLE));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_string (NM_SYSTEM_CONFIG_INTERFACE_INFO,
+							  "Info",
+							  "Plugin information",
+							  NULL,
+							  G_PARAM_READABLE));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_uint (NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES,
+							  "Capabilities",
+							  "Plugin capabilties",
+							  NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE,
+							  NM_SYSTEM_CONFIG_INTERFACE_CAP_LAST - 1,
+							  NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE,
+							  G_PARAM_READABLE));
+
+	g_object_interface_install_property
+		(g_iface,
+		 g_param_spec_string (NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME,
+							  "Hostname",
+							  "Configured hostname",
+							  NULL,
+							  G_PARAM_READWRITE));
+
+	/* Signals */
+	g_signal_new (NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED,
+				  iface_type,
+				  G_SIGNAL_RUN_FIRST,
+				  G_STRUCT_OFFSET (NMSystemConfigInterface, connection_added),
+				  NULL, NULL,
+				  g_cclosure_marshal_VOID__OBJECT,
+				  G_TYPE_NONE, 1,
+				  NM_TYPE_SETTINGS_CONNECTION_INTERFACE);
+
+	g_signal_new (NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED,
+				  iface_type,
+				  G_SIGNAL_RUN_FIRST,
+				  G_STRUCT_OFFSET (NMSystemConfigInterface, unmanaged_specs_changed),
+				  NULL, NULL,
+				  g_cclosure_marshal_VOID__VOID,
+				  G_TYPE_NONE, 0);
+
+	initialized = TRUE;
+}
+
+
+GType
+nm_system_config_interface_get_type (void)
+{
+	static GType system_config_interface_type = 0;
+
+	if (!system_config_interface_type) {
+		const GTypeInfo system_config_interface_info = {
+			sizeof (NMSystemConfigInterface), /* class_size */
+			interface_init,   /* base_init */
+			NULL,		/* base_finalize */
+			NULL,
+			NULL,		/* class_finalize */
+			NULL,		/* class_data */
+			0,
+			0,              /* n_preallocs */
+			NULL
+		};
+
+		system_config_interface_type = g_type_register_static (G_TYPE_INTERFACE,
+														       "NMSystemConfigInterface",
+														       &system_config_interface_info,
+														       0);
+
+		g_type_interface_add_prerequisite (system_config_interface_type, G_TYPE_OBJECT);
+	}
+
+	return system_config_interface_type;
+}
+
+void
+nm_system_config_interface_init (NMSystemConfigInterface *config,
+                                 gpointer unused)
+{
+	g_return_if_fail (config != NULL);
+
+	if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->init)
+		NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->init (config);
+}
+
+GSList *
+nm_system_config_interface_get_connections (NMSystemConfigInterface *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->get_connections)
+		return NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->get_connections (config);
+	return NULL;
+}
+
+GSList *
+nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->get_unmanaged_specs)
+		return NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->get_unmanaged_specs (config);
+	return NULL;
+}
+
+gboolean
+nm_system_config_interface_add_connection (NMSystemConfigInterface *config,
+                                           NMConnection *connection,
+                                           GError **error)
+{
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (config != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+	if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->add_connection)
+		success = NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->add_connection (config, connection, error);
+
+	return success;
+}
diff --git a/src/system-settings/nm-system-config-interface.h b/src/system-settings/nm-system-config-interface.h
new file mode 100644
index 00000000..c5bbaaa9
--- /dev/null
+++ b/src/system-settings/nm-system-config-interface.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * 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) 2007 - 2008 Red Hat, Inc.
+ * Copyright (C) 2008 Novell, Inc.
+ */
+
+#ifndef NM_SYSTEM_CONFIG_INTERFACE_H
+#define NM_SYSTEM_CONFIG_INTERFACE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <nm-connection.h>
+#include <nm-settings-connection-interface.h>
+
+G_BEGIN_DECLS
+
+#define PLUGIN_PRINT(pname, fmt, args...) \
+	{ g_message ("   " pname ": " fmt, ##args); }
+
+#define PLUGIN_WARN(pname, fmt, args...) \
+	{ g_warning ("   " pname ": " fmt, ##args); }
+
+
+/* Plugin's factory function that returns a GObject that implements
+ * NMSystemConfigInterface.
+ */
+GObject * nm_system_config_factory (void);
+
+/* NOTE:
+ *   When passing NMConnection objects to NetworkManager, any properties
+ * of that NMConnection's NMSetting objects that are secrets must be set as
+ * GObject data items on the NMSetting object, _not_ inside the NMSetting
+ * object itself.  This is to ensure that the secrets are only given to
+ * NetworkManager itself and not exposed to clients like nm-applet that need
+ * connection details, but not secrets.
+ */
+
+#define NM_TYPE_SYSTEM_CONFIG_INTERFACE      (nm_system_config_interface_get_type ())
+#define NM_SYSTEM_CONFIG_INTERFACE(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SYSTEM_CONFIG_INTERFACE, NMSystemConfigInterface))
+#define NM_IS_SYSTEM_CONFIG_INTERFACE(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SYSTEM_CONFIG_INTERFACE))
+#define NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_SYSTEM_CONFIG_INTERFACE, NMSystemConfigInterface))
+
+
+#define NM_SYSTEM_CONFIG_INTERFACE_NAME "name"
+#define NM_SYSTEM_CONFIG_INTERFACE_INFO "info"
+#define NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES "capabilities"
+#define NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME "hostname"
+
+#define NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED "unmanaged-specs-changed"
+#define NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED "connection-added"
+
+typedef enum {
+	NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE = 0x00000000,
+	NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS = 0x00000001,
+	NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME = 0x00000002,
+
+	NM_SYSTEM_CONFIG_INTERFACE_CAP_LAST = NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME
+} NMSystemConfigInterfaceCapabilities;
+
+typedef enum {
+	NM_SYSTEM_CONFIG_INTERFACE_PROP_FIRST = 0x1000,
+
+	NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME = NM_SYSTEM_CONFIG_INTERFACE_PROP_FIRST,
+	NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO,
+	NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES,
+	NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME,
+} NMSystemConfigInterfaceProp;
+
+
+typedef struct _NMSystemConfigInterface NMSystemConfigInterface;
+
+struct _NMSystemConfigInterface {
+	GTypeInterface g_iface;
+
+	/* Called when the plugin is loaded to initialize it */
+	void     (*init) (NMSystemConfigInterface *config);
+
+	/* Returns a GSList of objects that implement NMSettingsConnectionInterface
+	 * that represent connections the plugin knows about.  The returned list
+	 * is freed by the system settings service.
+	 */
+	GSList * (*get_connections) (NMSystemConfigInterface *config);
+
+	/*
+	 * Return a string list of specifications of devices which NetworkManager
+	 * should not manage.  Returned list will be freed by the system settings
+	 * service, and each element must be allocated using g_malloc() or its
+	 * variants (g_strdup, g_strdup_printf, etc).
+	 *
+	 * Each string in the list must follow the format <method>:<data>, where
+	 * the method and data are one of the following:
+	 *
+	 * Method: mac    Data: device MAC address formatted with leading zeros and
+	 *                      lowercase letters, like 00:0a:0b:0c:0d:0e
+	 */
+	GSList * (*get_unmanaged_specs) (NMSystemConfigInterface *config);
+
+	/*
+	 * Add a new connection.
+	 */
+	gboolean (*add_connection) (NMSystemConfigInterface *config,
+	                            NMConnection *connection,
+	                            GError **error);
+
+	/* Signals */
+
+	/* Emitted when a new connection has been found by the plugin */
+	void (*connection_added)   (NMSystemConfigInterface *config,
+	                            NMSettingsConnectionInterface *connection);
+
+	/* Emitted when the list of unmanaged device specifications changes */
+	void (*unmanaged_specs_changed) (NMSystemConfigInterface *config);
+};
+
+GType nm_system_config_interface_get_type (void);
+
+void nm_system_config_interface_init (NMSystemConfigInterface *config,
+                                      gpointer unused);
+
+GSList *nm_system_config_interface_get_connections (NMSystemConfigInterface *config);
+
+GSList *nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface *config);
+
+gboolean nm_system_config_interface_add_connection (NMSystemConfigInterface *config,
+                                                    NMConnection *connection,
+                                                    GError **error);
+
+G_END_DECLS
+
+#endif	/* NM_SYSTEM_CONFIG_INTERFACE_H */
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
new file mode 100644
index 00000000..62612a0e
--- /dev/null
+++ b/src/tests/Makefile.am
@@ -0,0 +1,49 @@
+INCLUDES = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/libnm-util \
+	-I$(top_srcdir)/src/dhcp-manager \
+	-I$(top_builddir)/marshallers \
+	-I$(top_srcdir)/src \
+	-I$(top_builddir)/src
+
+noinst_PROGRAMS = test-dhcp-options test-policy-hosts
+
+####### DHCP options test #######
+
+test_dhcp_options_SOURCES = \
+	test-dhcp-options.c
+
+test_dhcp_options_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
+	-DDHCPCD_PATH=\"$(DHCPCD_PATH)\"
+
+test_dhcp_options_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/src/dhcp-manager/libdhcp-manager.la \
+	$(top_builddir)/src/libtest-dhcp.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+####### policy /etc/hosts test #######
+
+test_policy_hosts_SOURCES = \
+	test-policy-hosts.c
+
+test_policy_hosts_CPPFLAGS = \
+	$(GLIB_CFLAGS)
+
+test_policy_hosts_LDADD = \
+	-ldl \
+	$(top_builddir)/src/libtest-policy-hosts.la \
+	$(GLIB_LIBS)
+
+if WITH_TESTS
+
+check-local: test-dhcp-options
+	$(abs_builddir)/test-dhcp-options
+	$(abs_builddir)/test-policy-hosts
+
+endif
+
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
new file mode 100644
index 00000000..75fb715d
--- /dev/null
+++ b/src/tests/Makefile.in
@@ -0,0 +1,628 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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@
+noinst_PROGRAMS = test-dhcp-options$(EXEEXT) \
+	test-policy-hosts$(EXEEXT)
+subdir = src/tests
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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_test_dhcp_options_OBJECTS =  \
+	test_dhcp_options-test-dhcp-options.$(OBJEXT)
+test_dhcp_options_OBJECTS = $(am_test_dhcp_options_OBJECTS)
+am__DEPENDENCIES_1 =
+test_dhcp_options_DEPENDENCIES =  \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/src/dhcp-manager/libdhcp-manager.la \
+	$(top_builddir)/src/libtest-dhcp.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+am_test_policy_hosts_OBJECTS =  \
+	test_policy_hosts-test-policy-hosts.$(OBJEXT)
+test_policy_hosts_OBJECTS = $(am_test_policy_hosts_OBJECTS)
+test_policy_hosts_DEPENDENCIES =  \
+	$(top_builddir)/src/libtest-policy-hosts.la \
+	$(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(test_dhcp_options_SOURCES) $(test_policy_hosts_SOURCES)
+DIST_SOURCES = $(test_dhcp_options_SOURCES) \
+	$(test_policy_hosts_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/libnm-util \
+	-I$(top_srcdir)/src/dhcp-manager \
+	-I$(top_builddir)/marshallers \
+	-I$(top_srcdir)/src \
+	-I$(top_builddir)/src
+
+
+####### DHCP options test #######
+test_dhcp_options_SOURCES = \
+	test-dhcp-options.c
+
+test_dhcp_options_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
+	-DDHCPCD_PATH=\"$(DHCPCD_PATH)\"
+
+test_dhcp_options_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(top_builddir)/src/dhcp-manager/libdhcp-manager.la \
+	$(top_builddir)/src/libtest-dhcp.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+
+####### policy /etc/hosts test #######
+test_policy_hosts_SOURCES = \
+	test-policy-hosts.c
+
+test_policy_hosts_CPPFLAGS = \
+	$(GLIB_CFLAGS)
+
+test_policy_hosts_LDADD = \
+	-ldl \
+	$(top_builddir)/src/libtest-policy-hosts.la \
+	$(GLIB_LIBS)
+
+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/tests/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/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
+test-dhcp-options$(EXEEXT): $(test_dhcp_options_OBJECTS) $(test_dhcp_options_DEPENDENCIES) 
+	@rm -f test-dhcp-options$(EXEEXT)
+	$(LINK) $(test_dhcp_options_OBJECTS) $(test_dhcp_options_LDADD) $(LIBS)
+test-policy-hosts$(EXEEXT): $(test_policy_hosts_OBJECTS) $(test_policy_hosts_DEPENDENCIES) 
+	@rm -f test-policy-hosts$(EXEEXT)
+	$(LINK) $(test_policy_hosts_OBJECTS) $(test_policy_hosts_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dhcp_options-test-dhcp-options.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_policy_hosts-test-policy-hosts.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+test_dhcp_options-test-dhcp-options.o: test-dhcp-options.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_dhcp_options_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_dhcp_options-test-dhcp-options.o -MD -MP -MF $(DEPDIR)/test_dhcp_options-test-dhcp-options.Tpo -c -o test_dhcp_options-test-dhcp-options.o `test -f 'test-dhcp-options.c' || echo '$(srcdir)/'`test-dhcp-options.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_dhcp_options-test-dhcp-options.Tpo $(DEPDIR)/test_dhcp_options-test-dhcp-options.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-dhcp-options.c' object='test_dhcp_options-test-dhcp-options.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_dhcp_options_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_dhcp_options-test-dhcp-options.o `test -f 'test-dhcp-options.c' || echo '$(srcdir)/'`test-dhcp-options.c
+
+test_dhcp_options-test-dhcp-options.obj: test-dhcp-options.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_dhcp_options_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_dhcp_options-test-dhcp-options.obj -MD -MP -MF $(DEPDIR)/test_dhcp_options-test-dhcp-options.Tpo -c -o test_dhcp_options-test-dhcp-options.obj `if test -f 'test-dhcp-options.c'; then $(CYGPATH_W) 'test-dhcp-options.c'; else $(CYGPATH_W) '$(srcdir)/test-dhcp-options.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_dhcp_options-test-dhcp-options.Tpo $(DEPDIR)/test_dhcp_options-test-dhcp-options.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-dhcp-options.c' object='test_dhcp_options-test-dhcp-options.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_dhcp_options_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_dhcp_options-test-dhcp-options.obj `if test -f 'test-dhcp-options.c'; then $(CYGPATH_W) 'test-dhcp-options.c'; else $(CYGPATH_W) '$(srcdir)/test-dhcp-options.c'; fi`
+
+test_policy_hosts-test-policy-hosts.o: test-policy-hosts.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_policy_hosts_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_policy_hosts-test-policy-hosts.o -MD -MP -MF $(DEPDIR)/test_policy_hosts-test-policy-hosts.Tpo -c -o test_policy_hosts-test-policy-hosts.o `test -f 'test-policy-hosts.c' || echo '$(srcdir)/'`test-policy-hosts.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_policy_hosts-test-policy-hosts.Tpo $(DEPDIR)/test_policy_hosts-test-policy-hosts.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-policy-hosts.c' object='test_policy_hosts-test-policy-hosts.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_policy_hosts_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_policy_hosts-test-policy-hosts.o `test -f 'test-policy-hosts.c' || echo '$(srcdir)/'`test-policy-hosts.c
+
+test_policy_hosts-test-policy-hosts.obj: test-policy-hosts.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_policy_hosts_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_policy_hosts-test-policy-hosts.obj -MD -MP -MF $(DEPDIR)/test_policy_hosts-test-policy-hosts.Tpo -c -o test_policy_hosts-test-policy-hosts.obj `if test -f 'test-policy-hosts.c'; then $(CYGPATH_W) 'test-policy-hosts.c'; else $(CYGPATH_W) '$(srcdir)/test-policy-hosts.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_policy_hosts-test-policy-hosts.Tpo $(DEPDIR)/test_policy_hosts-test-policy-hosts.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-policy-hosts.c' object='test_policy_hosts-test-policy-hosts.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_policy_hosts_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_policy_hosts-test-policy-hosts.obj `if test -f 'test-policy-hosts.c'; then $(CYGPATH_W) 'test-policy-hosts.c'; else $(CYGPATH_W) '$(srcdir)/test-policy-hosts.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+@WITH_TESTS_FALSE@check-local:
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-local
+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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+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 all all-am check check-am check-local clean \
+	clean-generic clean-libtool clean-noinstPROGRAMS ctags \
+	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 uninstall uninstall-am
+
+
+@WITH_TESTS_TRUE@check-local: test-dhcp-options
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-dhcp-options
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-policy-hosts
+
+# 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/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c
new file mode 100644
index 00000000..dd1f9149
--- /dev/null
+++ b/src/tests/test-dhcp-options.c
@@ -0,0 +1,715 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-dhcp-manager.c - Handle the DHCP daemon for 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) 2008 - 2010 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "nm-test-helpers.h"
+#include <nm-utils.h>
+
+#include "nm-dhcp-manager.h"
+
+typedef struct {
+	const char *name;
+	const char *value;
+} Option;
+
+static void
+destroy_gvalue (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+static GValue *
+string_to_byte_array_gvalue (const char *str)
+{
+	GByteArray *array;
+	GValue *val;
+
+	array = g_byte_array_sized_new (strlen (str));
+	g_byte_array_append (array, (const guint8 *) str, strlen (str));
+
+	val = g_slice_new0 (GValue);
+	g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY);
+	g_value_take_boxed (val, array);
+
+	return val;
+}
+
+static GHashTable *
+fill_table (Option *test_options, GHashTable *table)
+{
+	Option *opt;
+
+	if (!table)
+		table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, destroy_gvalue);
+	for (opt = test_options; opt->name; opt++) {
+		g_hash_table_insert (table,
+		                     (gpointer) opt->name,
+		                     string_to_byte_array_gvalue (opt->value));
+	}
+	return table;
+}
+
+static Option generic_options[] = {
+	{ "new_subnet_mask",            "255.255.255.0" },
+	{ "new_ip_address",             "192.168.1.106" },
+	{ "new_network_number",         "192.168.1.0" },
+	{ "interface",                  "eth0" },
+	{ "reason",                     "BOUND" },
+	{ "new_expiry",                 "1232324877" },
+	{ "new_dhcp_lease_time",        "3600" },
+	{ "new_dhcp_server_identifier", "192.168.1.1" },
+	{ "new_routers",                "192.168.1.1" },
+	{ "new_domain_name_servers",    "216.254.95.2 216.231.41.2" },
+	{ "new_dhcp_message_type",      "5" },
+	{ "new_broadcast_address",      "192.168.1.255" },
+	{ "new_domain_search",          "foobar.com blah.foobar.com" },
+	{ "new_host_name",              "nmreallywhipsthe" },
+	{ "new_domain_name",            "lamasass.com" },
+	{ "new_interface_mtu",          "987" },
+	{ "new_static_routes",          "10.1.1.5 10.1.1.1 100.99.88.56 10.1.1.1" },
+	{ NULL, NULL }
+};
+
+static void
+test_generic_options (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	NMIP4Address *addr;
+	NMIP4Route *route;
+	struct in_addr tmp;
+	const char *expected_addr = "192.168.1.106";
+	const char *expected_gw = "192.168.1.1";
+	const char *expected_dns1 = "216.254.95.2";
+	const char *expected_dns2 = "216.231.41.2";
+	const char *expected_search1 = "foobar.com";
+	const char *expected_search2 = "blah.foobar.com";
+	const char *expected_route1_dest = "10.1.1.5";
+	const char *expected_route1_gw = "10.1.1.1";
+	const char *expected_route2_dest = "100.99.88.56";
+	const char *expected_route2_gw = "10.1.1.1";
+
+	options = fill_table (generic_options, NULL);
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-generic", "failed to parse DHCP4 options");
+
+	/* IP4 address */
+	ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
+	        "dhcp-generic", "unexpected number of IP addresses");
+	addr = nm_ip4_config_get_address (ip4_config, 0);
+
+	ASSERT (inet_pton (AF_INET, expected_addr, &tmp) > 0,
+	        "dhcp-generic", "couldn't convert expected IP address");
+	ASSERT (nm_ip4_address_get_address (addr) == tmp.s_addr,
+	        "dhcp-generic", "unexpected IP address");
+
+	ASSERT (nm_ip4_address_get_prefix (addr) == 24,
+	        "dhcp-generic", "unexpected IP address prefix length");
+
+	/* Gateway */
+	ASSERT (inet_pton (AF_INET, expected_gw, &tmp) > 0,
+	        "dhcp-generic", "couldn't convert expected IP gateway");
+	ASSERT (nm_ip4_address_get_gateway (addr) == tmp.s_addr,
+	        "dhcp-generic", "unexpected IP gateway");
+
+	ASSERT (nm_ip4_config_get_ptp_address (ip4_config) == 0,
+	        "dhcp-generic", "unexpected PTP address");
+
+	ASSERT (nm_ip4_config_get_num_wins (ip4_config) == 0,
+	        "dhcp-generic", "unexpected number of WINS servers");
+
+	ASSERT (nm_ip4_config_get_mtu (ip4_config) == 987,
+	        "dhcp-generic", "unexpected MTU");
+
+	/* Domain searches */
+	ASSERT (nm_ip4_config_get_num_searches (ip4_config) == 2,
+	        "dhcp-generic", "unexpected number of domain searches");
+	ASSERT (strcmp (nm_ip4_config_get_search (ip4_config, 0), expected_search1) == 0,
+	        "dhcp-generic", "unexpected domain search #1");
+	ASSERT (strcmp (nm_ip4_config_get_search (ip4_config, 1), expected_search2) == 0,
+	        "dhcp-generic", "unexpected domain search #2");
+
+	/* DNS servers */
+	ASSERT (nm_ip4_config_get_num_nameservers (ip4_config) == 2,
+	        "dhcp-generic", "unexpected number of domain name servers");
+	ASSERT (inet_pton (AF_INET, expected_dns1, &tmp) > 0,
+	        "dhcp-generic", "couldn't convert expected DNS server address #1");
+	ASSERT (nm_ip4_config_get_nameserver (ip4_config, 0) == tmp.s_addr,
+	        "dhcp-generic", "unexpected domain name server #1");
+	ASSERT (inet_pton (AF_INET, expected_dns2, &tmp) > 0,
+	        "dhcp-generic", "couldn't convert expected DNS server address #2");
+	ASSERT (nm_ip4_config_get_nameserver (ip4_config, 1) == tmp.s_addr,
+	        "dhcp-generic", "unexpected domain name server #2");
+
+	/* Routes */
+	ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
+	        "dhcp-generic", "unexpected number of routes");
+
+	/* Route #1 */
+	route = nm_ip4_config_get_route (ip4_config, 0);
+	ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
+	        "dhcp-generic", "couldn't convert expected route destination #1");
+	ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+	        "dhcp-generic", "unexpected route #1 destination");
+
+	ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
+	        "dhcp-generic", "couldn't convert expected route next hop #1");
+	ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+	        "dhcp-generic", "unexpected route #1 next hop");
+
+	ASSERT (nm_ip4_route_get_prefix (route) == 32,
+	        "dhcp-generic", "unexpected route #1 prefix");
+	ASSERT (nm_ip4_route_get_metric (route) == 0,
+	        "dhcp-generic", "unexpected route #1 metric");
+
+	/* Route #2 */
+	route = nm_ip4_config_get_route (ip4_config, 1);
+	ASSERT (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0,
+	        "dhcp-generic", "couldn't convert expected route destination #2");
+	ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+	        "dhcp-generic", "unexpected route #2 destination");
+
+	ASSERT (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0,
+	        "dhcp-generic", "couldn't convert expected route next hop #2");
+	ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+	        "dhcp-generic", "unexpected route #2 next hop");
+
+	ASSERT (nm_ip4_route_get_prefix (route) == 32,
+	        "dhcp-generic", "unexpected route #2 prefix");
+	ASSERT (nm_ip4_route_get_metric (route) == 0,
+	        "dhcp-generic", "unexpected route #2 metric");
+
+	g_hash_table_destroy (options);
+}
+
+static Option wins_options[] = {
+	{ "new_netbios_name_servers", "63.12.199.5 150.4.88.120" },
+	{ NULL, NULL }
+};
+
+static void
+test_wins_options (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	NMIP4Address *addr;
+	struct in_addr tmp;
+	const char *expected_wins1 = "63.12.199.5";
+	const char *expected_wins2 = "150.4.88.120";
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (wins_options, options);
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-wins", "failed to parse DHCP4 options");
+
+	/* IP4 address */
+	ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
+	        "dhcp-wins", "unexpected number of IP addresses");
+	addr = nm_ip4_config_get_address (ip4_config, 0);
+
+	ASSERT (nm_ip4_config_get_num_wins (ip4_config) == 2,
+	        "dhcp-wins", "unexpected number of WINS servers");
+	ASSERT (inet_pton (AF_INET, expected_wins1, &tmp) > 0,
+	        "dhcp-wins", "couldn't convert expected WINS server address #1");
+	ASSERT (nm_ip4_config_get_wins (ip4_config, 0) == tmp.s_addr,
+	        "dhcp-wins", "unexpected WINS server #1");
+	ASSERT (inet_pton (AF_INET, expected_wins2, &tmp) > 0,
+	        "dhcp-wins", "couldn't convert expected WINS server address #1");
+	ASSERT (nm_ip4_config_get_wins (ip4_config, 1) == tmp.s_addr,
+	        "dhcp-wins", "unexpected WINS server #1");
+
+	g_hash_table_destroy (options);
+}
+
+static Option classless_routes_options[] = {
+	/* For dhclient */
+	{ "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41" },
+	/* For dhcpcd */
+	{ "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41" },
+	{ NULL, NULL }
+};
+
+static void
+test_classless_static_routes (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	NMIP4Route *route;
+	struct in_addr tmp;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+	const char *expected_route2_dest = "10.0.0.0";
+	const char *expected_route2_gw = "10.17.66.41";
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (classless_routes_options, options);
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-rfc3442", "failed to parse DHCP4 options");
+
+	/* IP4 routes */
+	ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
+	        "dhcp-rfc3442", "unexpected number of IP routes");
+
+	/* Route #1 */
+	route = nm_ip4_config_get_route (ip4_config, 0);
+	ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
+	        "dhcp-rfc3442", "couldn't convert expected route destination #1");
+	ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+	        "dhcp-rfc3442", "unexpected route #1 destination");
+
+	ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
+	        "dhcp-rfc3442", "couldn't convert expected route next hop #1");
+	ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+	        "dhcp-rfc3442", "unexpected route #1 next hop");
+
+	ASSERT (nm_ip4_route_get_prefix (route) == 24,
+	        "dhcp-rfc3442", "unexpected route #1 prefix");
+	ASSERT (nm_ip4_route_get_metric (route) == 0,
+	        "dhcp-rfc3442", "unexpected route #1 metric");
+
+	/* Route #2 */
+	route = nm_ip4_config_get_route (ip4_config, 1);
+	ASSERT (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0,
+	        "dhcp-rfc3442", "couldn't convert expected route destination #2");
+	ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+	        "dhcp-rfc3442", "unexpected route #2 destination");
+
+	ASSERT (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0,
+	        "dhcp-rfc3442", "couldn't convert expected route next hop #2");
+	ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+	        "dhcp-rfc3442", "unexpected route #2 next hop");
+
+	ASSERT (nm_ip4_route_get_prefix (route) == 8,
+	        "dhcp-rfc3442", "unexpected route #2 prefix");
+	ASSERT (nm_ip4_route_get_metric (route) == 0,
+	        "dhcp-rfc3442", "unexpected route #2 metric");
+
+	g_hash_table_destroy (options);
+}
+
+static Option invalid_classless_routes1[] = {
+	/* For dhclient */
+	{ "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41" },
+	/* For dhcpcd */
+	{ "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41" },
+	{ NULL, NULL }
+};
+
+static void
+test_invalid_classless_routes1 (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	NMIP4Route *route;
+	struct in_addr tmp;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (invalid_classless_routes1, options);
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-rfc3442-invalid-1", "failed to parse DHCP4 options");
+
+	/* IP4 routes */
+	ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
+	        "dhcp-rfc3442-invalid-1", "unexpected number of IP routes");
+
+	/* Route #1 */
+	route = nm_ip4_config_get_route (ip4_config, 0);
+	ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
+	        "dhcp-rfc3442-invalid-1", "couldn't convert expected route destination #1");
+	ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+	        "dhcp-rfc3442-invalid-1", "unexpected route #1 destination");
+
+	ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
+	        "dhcp-rfc3442-invalid-1", "couldn't convert expected route next hop #1");
+	ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+	        "dhcp-rfc3442-invalid-1", "unexpected route #1 next hop");
+
+	ASSERT (nm_ip4_route_get_prefix (route) == 24,
+	        "dhcp-rfc3442-invalid-1", "unexpected route #1 prefix");
+	ASSERT (nm_ip4_route_get_metric (route) == 0,
+	        "dhcp-rfc3442-invalid-1", "unexpected route #1 metric");
+
+	g_hash_table_destroy (options);
+}
+
+static Option invalid_classless_routes2[] = {
+	/* For dhclient */
+	{ "new_rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1" },
+	/* For dhcpcd */
+	{ "new_classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1" },
+	{ NULL, NULL }
+};
+
+static void
+test_invalid_classless_routes2 (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	NMIP4Route *route;
+	struct in_addr tmp;
+	const char *expected_route1_dest = "10.1.1.5";
+	const char *expected_route1_gw = "10.1.1.1";
+	const char *expected_route2_dest = "100.99.88.56";
+	const char *expected_route2_gw = "10.1.1.1";
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (invalid_classless_routes2, options);
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-rfc3442-invalid-2", "failed to parse DHCP4 options");
+
+	/* Test falling back to old-style static routes if the classless static
+	 * routes are invalid.
+	 */
+
+	/* Routes */
+	ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
+	        "dhcp-rfc3442-invalid-2", "unexpected number of routes");
+
+	/* Route #1 */
+	route = nm_ip4_config_get_route (ip4_config, 0);
+	ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
+	        "dhcp-rfc3442-invalid-2", "couldn't convert expected route destination #1");
+	ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+	        "dhcp-rfc3442-invalid-2", "unexpected route #1 destination");
+
+	ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
+	        "dhcp-rfc3442-invalid-2", "couldn't convert expected route next hop #1");
+	ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+	        "dhcp-rfc3442-invalid-2", "unexpected route #1 next hop");
+
+	ASSERT (nm_ip4_route_get_prefix (route) == 32,
+	        "dhcp-rfc3442-invalid-2", "unexpected route #1 prefix");
+	ASSERT (nm_ip4_route_get_metric (route) == 0,
+	        "dhcp-rfc3442-invalid-2", "unexpected route #1 metric");
+
+	/* Route #2 */
+	route = nm_ip4_config_get_route (ip4_config, 1);
+	ASSERT (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0,
+	        "dhcp-rfc3442-invalid-2", "couldn't convert expected route destination #2");
+	ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+	        "dhcp-rfc3442-invalid-2", "unexpected route #2 destination");
+
+	ASSERT (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0,
+	        "dhcp-rfc3442-invalid-2", "couldn't convert expected route next hop #2");
+	ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+	        "dhcp-rfc3442-invalid-2", "unexpected route #2 next hop");
+
+	ASSERT (nm_ip4_route_get_prefix (route) == 32,
+	        "dhcp-rfc3442-invalid-2", "unexpected route #2 prefix");
+	ASSERT (nm_ip4_route_get_metric (route) == 0,
+	        "dhcp-rfc3442-invalid-2", "unexpected route #2 metric");
+
+	g_hash_table_destroy (options);
+}
+
+static Option invalid_classless_routes3[] = {
+	/* For dhclient */
+	{ "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41" },
+	/* For dhcpcd */
+	{ "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41" },
+	{ NULL, NULL }
+};
+
+static void
+test_invalid_classless_routes3 (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	NMIP4Route *route;
+	struct in_addr tmp;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (invalid_classless_routes3, options);
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-rfc3442-invalid-3", "failed to parse DHCP4 options");
+
+	/* IP4 routes */
+	ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
+	        "dhcp-rfc3442-invalid-3", "unexpected number of IP routes");
+
+	/* Route #1 */
+	route = nm_ip4_config_get_route (ip4_config, 0);
+	ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
+	        "dhcp-rfc3442-invalid-3", "couldn't convert expected route destination #1");
+	ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+	        "dhcp-rfc3442-invalid-3", "unexpected route #1 destination");
+
+	ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
+	        "dhcp-rfc3442-invalid-3", "couldn't convert expected route next hop #1");
+	ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+	        "dhcp-rfc3442-invalid-3", "unexpected route #1 next hop");
+
+	ASSERT (nm_ip4_route_get_prefix (route) == 24,
+	        "dhcp-rfc3442-invalid-3", "unexpected route #1 prefix");
+	ASSERT (nm_ip4_route_get_metric (route) == 0,
+	        "dhcp-rfc3442-invalid-3", "unexpected route #1 metric");
+
+	g_hash_table_destroy (options);
+}
+
+static Option gw_in_classless_routes[] = {
+	/* For dhclient */
+	{ "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4" },
+	/* For dhcpcd */
+	{ "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4" },
+	{ NULL, NULL }
+};
+
+static void
+test_gateway_in_classless_routes (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	NMIP4Address *addr;
+	NMIP4Route *route;
+	struct in_addr tmp;
+	const char *expected_route1_dest = "192.168.10.0";
+	const char *expected_route1_gw = "192.168.1.1";
+	const char *expected_gateway = "192.2.3.4";
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (gw_in_classless_routes, options);
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-rfc3442-gateway", "failed to parse DHCP4 options");
+
+	/* IP4 routes */
+	ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
+	        "dhcp-rfc3442-gateway", "unexpected number of IP routes");
+
+	/* Route #1 */
+	route = nm_ip4_config_get_route (ip4_config, 0);
+	ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
+	        "dhcp-rfc3442-gateway", "couldn't convert expected route destination #1");
+	ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+	        "dhcp-rfc3442-gateway", "unexpected route #1 destination");
+
+	ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
+	        "dhcp-rfc3442-gateway", "couldn't convert expected route next hop #1");
+	ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+	        "dhcp-rfc3442-gateway", "unexpected route #1 next hop");
+
+	ASSERT (nm_ip4_route_get_prefix (route) == 24,
+	        "dhcp-rfc3442-gateway", "unexpected route #1 prefix");
+	ASSERT (nm_ip4_route_get_metric (route) == 0,
+	        "dhcp-rfc3442-gateway", "unexpected route #1 metric");
+
+	/* Address */
+	ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
+	        "dhcp-rfc3442-gateway", "unexpected number of IP addresses");
+	addr = nm_ip4_config_get_address (ip4_config, 0);
+	ASSERT (inet_pton (AF_INET, expected_gateway, &tmp) > 0,
+	        "dhcp-rfc3442-gateway", "couldn't convert expected IP gateway");
+	ASSERT (nm_ip4_address_get_gateway (addr) == tmp.s_addr,
+	        "dhcp-rfc3442-gateway", "unexpected IP gateway");
+
+	g_hash_table_destroy (options);
+}
+
+static Option escaped_searches_options[] = {
+	{ "new_domain_search", "host1\\032host2\\032host3" },
+	{ NULL, NULL }
+};
+
+static void
+test_escaped_domain_searches (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	const char *expected_search0 = "host1";
+	const char *expected_search1 = "host2";
+	const char *expected_search2 = "host3";
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (escaped_searches_options, options);
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-escaped-domain-searches", "failed to parse DHCP4 options");
+
+	/* domain searches */
+	ASSERT (nm_ip4_config_get_num_searches (ip4_config) == 3,
+	        "dhcp-escaped-domain-searches", "unexpected number of searches");
+	ASSERT (!strcmp (nm_ip4_config_get_search (ip4_config, 0), expected_search0),
+	        "dhcp-escaped-domain-searches", "unexpected domain search #1");
+	ASSERT (!strcmp (nm_ip4_config_get_search (ip4_config, 1), expected_search1),
+	        "dhcp-escaped-domain-searches", "unexpected domain search #1");
+	ASSERT (!strcmp (nm_ip4_config_get_search (ip4_config, 2), expected_search2),
+	        "dhcp-escaped-domain-searches", "unexpected domain search #1");
+
+	g_hash_table_destroy (options);
+}
+
+static Option invalid_escaped_searches_options[] = {
+	{ "new_domain_search", "host1\\aahost2\\032host3" },
+	{ NULL, NULL }
+};
+
+static void
+test_invalid_escaped_domain_searches (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (invalid_escaped_searches_options, options);
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-invalid-escaped-domain-searches", "failed to parse DHCP4 options");
+
+	/* domain searches */
+	ASSERT (nm_ip4_config_get_num_searches (ip4_config) == 0,
+	        "dhcp-invalid-escaped-domain-searches", "unexpected domain searches");
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_ip4_missing_prefix (const char *client, const char *ip, guint32 expected_prefix)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	NMIP4Address *addr;
+
+	options = fill_table (generic_options, NULL);
+	g_hash_table_insert (options, "new_ip_address", string_to_byte_array_gvalue (ip));
+	g_hash_table_remove (options, "new_subnet_mask");
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-ip4-missing-prefix", "failed to parse DHCP4 options");
+
+	ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
+	        "dhcp-ip4-missing-prefix", "unexpected number of IP4 addresses (not 1)");
+
+	addr = nm_ip4_config_get_address (ip4_config, 0);
+	ASSERT (addr != NULL,
+	        "dhcp-ip4-missing-prefix", "missing IP4 address #1");
+
+	ASSERT (nm_ip4_address_get_prefix (addr) == expected_prefix,
+	        "dhcp-ip4-missing-prefix", "unexpected IP4 address prefix %d (expected %d)",
+	        nm_ip4_address_get_prefix (addr), expected_prefix);
+
+	g_hash_table_destroy (options);
+}
+
+static void
+test_ip4_prefix_classless (const char *client)
+{
+	GHashTable *options;
+	NMIP4Config *ip4_config;
+	NMIP4Address *addr;
+
+	/* Ensure that the missing-subnet-mask handler doesn't mangle classless
+	 * subnet masks at all.  The handler should trigger only if the server
+	 * doesn't send the subnet mask.
+	 */
+
+	options = fill_table (generic_options, NULL);
+	g_hash_table_insert (options, "new_ip_address", string_to_byte_array_gvalue ("172.16.54.22"));
+	g_hash_table_insert (options, "new_subnet_mask", string_to_byte_array_gvalue ("255.255.252.0"));
+
+	ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+	ASSERT (ip4_config != NULL,
+	        "dhcp-ip4-prefix-classless", "failed to parse DHCP4 options");
+
+	ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
+	        "dhcp-ip4-prefix-classless", "unexpected number of IP4 addresses (not 1)");
+
+	addr = nm_ip4_config_get_address (ip4_config, 0);
+	ASSERT (addr != NULL,
+	        "dhcp-ip4-prefix-classless", "missing IP4 address #1");
+
+	ASSERT (nm_ip4_address_get_prefix (addr) == 22,
+	        "dhcp-ip4-prefix-classless", "unexpected IP4 address prefix %d (expected 22)",
+	        nm_ip4_address_get_prefix (addr));
+
+	g_hash_table_destroy (options);
+}
+
+int main (int argc, char **argv)
+{
+	GError *error = NULL;
+	DBusGConnection *bus;
+	char *base;
+	const char *clients[2][2] = { {DHCLIENT_PATH, "dhclient"}, {DHCPCD_PATH, "dhcpcd"} };
+	guint32 i;
+
+	g_type_init ();
+	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+
+	if (!nm_utils_init (&error))
+		FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+
+	/* The tests */
+	for (i = 0; i < 2; i++) {
+		const char *client_path = clients[i][0];
+		const char *client = clients[i][1];
+
+		if (!client_path || !strlen (client_path))
+			continue;
+
+		test_generic_options (client);
+		test_wins_options (client);
+		test_classless_static_routes (client);
+		test_invalid_classless_routes1 (client);
+		test_invalid_classless_routes2 (client);
+		test_invalid_classless_routes3 (client);
+		test_gateway_in_classless_routes (client);
+		test_escaped_domain_searches (client);
+		test_invalid_escaped_domain_searches (client);
+		test_ip4_missing_prefix (client, "192.168.1.10", 24);
+		test_ip4_missing_prefix (client, "172.16.54.50", 16);
+		test_ip4_missing_prefix (client, "10.1.2.3", 8);
+		test_ip4_prefix_classless (client);
+	}
+
+	base = g_path_get_basename (argv[0]);
+	fprintf (stdout, "%s: SUCCESS\n", base);
+	g_free (base);
+	return 0;
+}
+
diff --git a/src/tests/test-policy-hosts.c b/src/tests/test-policy-hosts.c
new file mode 100644
index 00000000..e14f15e5
--- /dev/null
+++ b/src/tests/test-policy-hosts.c
@@ -0,0 +1,394 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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) 2010 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "nm-policy-hosts.h"
+
+#define FALLBACK_HOSTNAME "localhost.localdomain"
+
+static void
+test_generic (const char *before,
+              const char *after,
+              const char *hostname,
+              gboolean expect_error)
+{
+	char **lines;
+	GString *newc;
+	GError *error = NULL;
+
+	/* Get the new /etc/hosts contents */
+	lines = g_strsplit_set (before, "\n\r", 0);
+	newc = nm_policy_get_etc_hosts ((const char **) lines,
+	                                strlen (before),
+	                                hostname,
+	                                FALLBACK_HOSTNAME,
+	                                &error);
+	g_strfreev (lines);
+
+	if (expect_error) {
+		g_assert (newc == NULL);
+		g_assert (error != NULL);
+		g_clear_error (&error);
+	} else if (after == NULL) {
+		/* No change to /etc/hosts required */
+		g_assert (newc == NULL);
+		g_assert (error == NULL);
+	} else {
+		g_assert (newc != NULL);
+		g_assert (error == NULL);
+
+#if 0
+		g_message ("\n--------------------------------------\n"
+		           "%s"
+		           "--------------------------------------",
+		           newc->str);
+#endif
+		g_assert (strlen (newc->str) == strlen (after));
+		g_assert (strcmp (newc->str, after) == 0);
+		g_string_free (newc, TRUE);
+	}
+}
+
+/*******************************************/
+
+static const char *generic_before = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	localhost.localdomain localhost\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n";
+
+static void
+test_hosts_generic (void)
+{
+	test_generic (generic_before, NULL, "localhost.localdomain", FALSE);
+}
+
+/*******************************************/
+
+static const char *generic_no_boilerplate_before = \
+	"127.0.0.1	localhost.localdomain localhost\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n";
+
+static void
+test_hosts_generic_no_boilerplate (void)
+{
+	test_generic (generic_no_boilerplate_before, NULL, "localhost.localdomain", FALSE);
+}
+
+/*******************************************/
+
+static const char *generic_no_boilerplate_no_lh_before = \
+	"127.0.0.1	localhost.localdomain\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n";
+
+static const char *generic_no_boilerplate_no_lh_after = \
+	"127.0.0.1	localhost\n"
+	"127.0.0.1	localhost.localdomain\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n";
+
+static void
+test_hosts_generic_no_boilerplate_no_lh (void)
+{
+	test_generic (generic_no_boilerplate_no_lh_before,
+	              generic_no_boilerplate_no_lh_after,
+	              "localhost.localdomain",
+	              FALSE);
+}
+
+/*******************************************/
+
+
+static const char *generic_no_boilerplate_no_lh_no_host_before = \
+	"127.0.0.1	localhost.localdomain\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n";
+
+static const char *generic_no_boilerplate_no_lh_no_host_after = \
+	"127.0.0.1	comet	localhost.localdomain	localhost\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n";
+
+static void
+test_hosts_generic_no_boilerplate_no_lh_no_host (void)
+{
+	test_generic (generic_no_boilerplate_no_lh_no_host_before,
+	              generic_no_boilerplate_no_lh_no_host_after,
+	              "comet",
+	              FALSE);
+}
+
+/*******************************************/
+static const char *named_generic_before = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	playboy localhost\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n";
+
+static void
+test_hosts_named_generic (void)
+{
+	test_generic (named_generic_before, NULL, "playboy", FALSE);
+}
+
+/*******************************************/
+
+static const char *named_non127_before = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	localhost.localdomain localhost\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n"
+	"192.168.1.2	tomcat\n";
+
+static void
+test_hosts_named_non127 (void)
+{
+	test_generic (named_non127_before, NULL, "tomcat", FALSE);
+}
+
+/*******************************************/
+
+static const char *named2_non127_before = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	localhost.localdomain localhost\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n"
+	"192.168.1.2	tomcat\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n"
+	"127.0.0.1	srx.main.ebayrtm.com\n"
+	"127.0.0.1	cdn5.tribalfusion.com\n";
+
+static void
+test_hosts_named2_non127 (void)
+{
+	test_generic (named2_non127_before, NULL, "tomcat", FALSE);
+}
+
+/*******************************************/
+
+static const char *named_no_lh_before = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	localhost.localdomain\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n"
+	"192.168.1.2	tomcat\n";
+
+static const char *named_no_lh_after = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	localhost.localdomain	localhost\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n"
+	"192.168.1.2	tomcat\n";
+
+static void
+test_hosts_named_no_localhost (void)
+{
+	test_generic (named_no_lh_before, named_no_lh_after, "tomcat", FALSE);
+}
+
+/*******************************************/
+
+static const char *no_lh_before = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	tomcat\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n";
+
+static const char *no_lh_after = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	localhost.localdomain	localhost\n"
+	"127.0.0.1	tomcat\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n";
+
+static void
+test_hosts_no_localhost (void)
+{
+	test_generic (no_lh_before, no_lh_after, "tomcat", FALSE);
+}
+
+/*******************************************/
+
+static const char *named_last_before = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4 sparcbook.ausil.us\n"
+	"::1         localhost localhost.localdomain localhost6 localhost6.localdomain6 sparcbook.ausil.us\n";
+
+static void
+test_hosts_named_last (void)
+{
+	test_generic (named_last_before, NULL, "sparcbook.ausil.us", FALSE);
+}
+
+/*******************************************/
+
+static const char *no_host_before = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n"
+	"127.0.0.1	srx.main.ebayrtm.com\n"
+	"127.0.0.1	cdn5.tribalfusion.com\n"
+	"127.0.0.1	a.tribalfusion.com\n";
+
+static const char *no_host_after = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	comet	localhost.localdomain	localhost\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n"
+	"127.0.0.1	srx.main.ebayrtm.com\n"
+	"127.0.0.1	cdn5.tribalfusion.com\n"
+	"127.0.0.1	a.tribalfusion.com\n";
+
+static void
+test_hosts_no_host (void)
+{
+	test_generic (no_host_before, no_host_after, "comet", FALSE);
+}
+
+/*******************************************/
+
+static const char *long_before = \
+	"# Do not remove the following line, or various programs\n"
+	"# that require network functionality will fail.\n"
+	"127.0.0.1	localhost.localdomain	localhost	comet\n"
+	"::1		localhost6.localdomain6 localhost6\n"
+	"\n"
+	"127.0.0.1	lcmd.us.intellitxt.com\n"
+	"127.0.0.1	adserver.adtech.de\n"
+	"127.0.0.1	a.as-us.falkag.net\n"
+	"127.0.0.1	a.as-eu.falkag.net\n"
+	"127.0.0.1	ads.doubleclick.com\n"
+	"\n"
+	"# random comment\n"
+	"127.0.0.1	m1.2mdn.net\n"
+	"127.0.0.1	ds.serving-sys.com\n"
+	"127.0.0.1	pagead2.googlesyndication.com\n"
+	"127.0.0.1	ad.doubleclick.com\n"
+	"127.0.0.1	ad.doubleclick.net\n"
+	"127.0.0.1	oascentral.movietickets.com\n"
+	"127.0.0.1	view.atdmt.com\n"
+	"127.0.0.1	ads.chumcity.com\n"
+	"127.0.0.1	ads.as4x.tmcs.net\n"
+	"127.0.0.1	n4403ad.doubleclick.net\n"
+	"127.0.0.1	www.assoc-amazon.com\n"
+	"127.0.0.1	s25.sitemeter.com\n"
+	"127.0.0.1	adlog.com.com\n"
+	"127.0.0.1	ahs.laptopmag.com\n"
+	"127.0.0.1	altfarm.mediaplex.com\n"
+	"127.0.0.1	ads.addynamix.com\n"
+	"127.0.0.1	srx.main.ebayrtm.com\n"
+	"127.0.0.1	cdn5.tribalfusion.com\n"
+	"127.0.0.1	a.tribalfusion.com\n";
+
+
+static void
+test_hosts_long (void)
+{
+	test_generic (long_before, NULL, "comet", FALSE);
+}
+
+/*******************************************/
+
+typedef struct {
+	const char *line;
+	const char *token;
+	gboolean expected;
+} Foo;
+
+static Foo foo[] = {
+	/* Using \t here to easily differentiate tabs vs. spaces for testing */
+	{ "127.0.0.1\tfoobar\tblah", "blah", TRUE },
+	{ "", "blah", FALSE },
+	{ "1.1.1.1\tbork\tfoo", "blah", FALSE },
+	{ "127.0.0.1 foobar\tblah", "blah", TRUE },
+	{ "127.0.0.1 foobar blah", "blah", TRUE },
+	{ "127.0.0.1 localhost", "localhost.localdomain", FALSE },
+	{ "192.168.1.1 blah borkbork", "blah", TRUE },
+	{ "192.168.1.1 foobar\tblah borkbork", "blah", TRUE },
+	{ "192.168.1.1\tfoobar\tblah\tborkbork", "blah", TRUE },
+	{ "192.168.1.1 \tfoobar \tblah \tborkbork\t ", "blah", TRUE },
+	{ "\t\t\t\t   \t\t\tasdfadf  a\t\t\t\t\t   \t\t\t\t\t ", "blah", FALSE },
+	{ NULL, NULL, FALSE }
+};
+
+static void
+test_find_token (void)
+{
+	Foo *iter = &foo[0];
+
+	while (iter->line) {
+		gboolean found;
+
+		found = nm_policy_hosts_find_token (iter->line, iter->token);
+		if (found != iter->expected) {
+			g_warning ("find-token: unexpected token result %d for '%s' <= '%s' (expected %d)",
+			           found, iter->line, iter->token, iter->expected);
+		}
+		g_assert (found == iter->expected);
+		iter++;
+	}
+}
+
+typedef void (*TCFunc)(void);
+
+#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL)
+
+int main (int argc, char **argv)
+{
+	GTestSuite *suite;
+
+	g_test_init (&argc, &argv, NULL);
+
+	suite = g_test_get_root ();
+
+	g_test_suite_add (suite, TESTCASE (test_find_token, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_generic, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate_no_lh, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate_no_lh_no_host, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_named_generic, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_named_non127, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_named2_non127, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_named_no_localhost, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_no_localhost, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_named_last, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_no_host, NULL));
+	g_test_suite_add (suite, TESTCASE (test_hosts_long, NULL));
+
+	return g_test_run ();
+}
+
diff --git a/src/vpn-manager/Makefile.am b/src/vpn-manager/Makefile.am
new file mode 100644
index 00000000..56e4a4ce
--- /dev/null
+++ b/src/vpn-manager/Makefile.am
@@ -0,0 +1,46 @@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src \
+	-I${top_builddir}/marshallers \
+	-I${top_srcdir}/src/named-manager \
+	-DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\"
+
+
+noinst_LTLIBRARIES = libvpn-manager.la
+
+libvpn_manager_la_SOURCES =	\
+	nm-vpn-manager.c	\
+	nm-vpn-manager.h	\
+	nm-vpn-service.c	\
+	nm-vpn-service.h	\
+	nm-vpn-connection.c	\
+	nm-vpn-connection.h
+
+libvpn_manager_la_CPPFLAGS =	\
+	$(DBUS_CFLAGS)		\
+	$(GLIB_CFLAGS)		\
+	-DG_DISABLE_DEPRECATED
+
+libvpn_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+nm-vpn-connection-glue.h: $(top_srcdir)/introspection/nm-vpn-connection.xml
+	dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $<
+
+nm-vpn-plugin-bindings.h: $(top_srcdir)/introspection/nm-vpn-plugin.xml
+	dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $<
+
+
+BUILT_SOURCES = \
+	nm-vpn-connection-glue.h \
+	nm-vpn-plugin-bindings.h
+
+CLEANFILES = $(BUILT_SOURCES)
+
diff --git a/src/vpn-manager/Makefile.in b/src/vpn-manager/Makefile.in
new file mode 100644
index 00000000..dc1ba727
--- /dev/null
+++ b/src/vpn-manager/Makefile.in
@@ -0,0 +1,611 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  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@
+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/vpn-manager
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.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)/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 =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libvpn_manager_la_DEPENDENCIES =  \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+am_libvpn_manager_la_OBJECTS = libvpn_manager_la-nm-vpn-manager.lo \
+	libvpn_manager_la-nm-vpn-service.lo \
+	libvpn_manager_la-nm-vpn-connection.lo
+libvpn_manager_la_OBJECTS = $(am_libvpn_manager_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libvpn_manager_la_SOURCES)
+DIST_SOURCES = $(libvpn_manager_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+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@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+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@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = \
+	-I${top_srcdir} \
+	-I${top_srcdir}/include \
+	-I${top_srcdir}/libnm-util \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src \
+	-I${top_builddir}/marshallers \
+	-I${top_srcdir}/src/named-manager \
+	-DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\"
+
+noinst_LTLIBRARIES = libvpn-manager.la
+libvpn_manager_la_SOURCES = \
+	nm-vpn-manager.c	\
+	nm-vpn-manager.h	\
+	nm-vpn-service.c	\
+	nm-vpn-service.h	\
+	nm-vpn-connection.c	\
+	nm-vpn-connection.h
+
+libvpn_manager_la_CPPFLAGS = \
+	$(DBUS_CFLAGS)		\
+	$(GLIB_CFLAGS)		\
+	-DG_DISABLE_DEPRECATED
+
+libvpn_manager_la_LIBADD = \
+	$(top_builddir)/marshallers/libmarshallers.la \
+	$(top_builddir)/src/logging/libnm-logging.la \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(DBUS_LIBS) \
+	$(GLIB_LIBS)
+
+BUILT_SOURCES = \
+	nm-vpn-connection-glue.h \
+	nm-vpn-plugin-bindings.h
+
+CLEANFILES = $(BUILT_SOURCES)
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) 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/vpn-manager/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/vpn-manager/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-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libvpn-manager.la: $(libvpn_manager_la_OBJECTS) $(libvpn_manager_la_DEPENDENCIES) 
+	$(LINK)  $(libvpn_manager_la_OBJECTS) $(libvpn_manager_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-service.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libvpn_manager_la-nm-vpn-manager.lo: nm-vpn-manager.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-manager.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Tpo -c -o libvpn_manager_la-nm-vpn-manager.lo `test -f 'nm-vpn-manager.c' || echo '$(srcdir)/'`nm-vpn-manager.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-vpn-manager.c' object='libvpn_manager_la-nm-vpn-manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-manager.lo `test -f 'nm-vpn-manager.c' || echo '$(srcdir)/'`nm-vpn-manager.c
+
+libvpn_manager_la-nm-vpn-service.lo: nm-vpn-service.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-service.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Tpo -c -o libvpn_manager_la-nm-vpn-service.lo `test -f 'nm-vpn-service.c' || echo '$(srcdir)/'`nm-vpn-service.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-vpn-service.c' object='libvpn_manager_la-nm-vpn-service.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-service.lo `test -f 'nm-vpn-service.c' || echo '$(srcdir)/'`nm-vpn-service.c
+
+libvpn_manager_la-nm-vpn-connection.lo: nm-vpn-connection.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-connection.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Tpo -c -o libvpn_manager_la-nm-vpn-connection.lo `test -f 'nm-vpn-connection.c' || echo '$(srcdir)/'`nm-vpn-connection.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-vpn-connection.c' object='libvpn_manager_la-nm-vpn-connection.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-connection.lo `test -f 'nm-vpn-connection.c' || echo '$(srcdir)/'`nm-vpn-connection.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	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"
+
+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
+check-am: all-am
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) 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:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+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."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+	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: all check install install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES ctags 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 uninstall uninstall-am
+
+
+nm-vpn-connection-glue.h: $(top_srcdir)/introspection/nm-vpn-connection.xml
+	dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $<
+
+nm-vpn-plugin-bindings.h: $(top_srcdir)/introspection/nm-vpn-plugin.xml
+	dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $<
+
+# 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/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
new file mode 100644
index 00000000..7eae972e
--- /dev/null
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -0,0 +1,1192 @@
+/* -*- 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 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "NetworkManager.h"
+#include "NetworkManagerVPN.h"
+#include "nm-vpn-connection.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-vpn.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-dbus-manager.h"
+#include "nm-manager.h"
+#include "nm-system.h"
+#include "nm-logging.h"
+#include "nm-utils.h"
+#include "nm-vpn-plugin-bindings.h"
+#include "nm-marshal.h"
+#include "nm-active-connection.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-dbus-glib-types.h"
+#include "NetworkManagerUtils.h"
+#include "nm-named-manager.h"
+#include "nm-netlink-monitor.h"
+#include "nm-glib-compat.h"
+
+#include "nm-vpn-connection-glue.h"
+
+static void secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class);
+
+G_DEFINE_TYPE_EXTENDED (NMVPNConnection, nm_vpn_connection, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_SECRETS_PROVIDER_INTERFACE,
+                                               secrets_provider_interface_init))
+
+typedef struct {
+	gboolean disposed;
+
+	NMConnection *connection;
+
+	NMActRequest *act_request;
+	char *ac_path;
+
+	NMDevice *parent_dev;
+	gulong device_monitor;
+	gulong device_ip4;
+
+	gboolean is_default;
+	gboolean is_default6;
+	NMActiveConnectionState state;
+
+	NMVPNConnectionState vpn_state;
+	NMVPNConnectionStateReason failure_reason;
+	DBusGProxy *proxy;
+	guint ipconfig_timeout;
+	NMIP4Config *ip4_config;
+	guint32 ip4_internal_gw;
+	char *tundev;
+	char *banner;
+
+	struct rtnl_route *gw_route;
+} NMVPNConnectionPrivate;
+
+#define NM_VPN_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_CONNECTION, NMVPNConnectionPrivate))
+
+enum {
+	PROPERTIES_CHANGED,
+	VPN_STATE_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_0,
+	PROP_SERVICE_NAME,
+	PROP_CONNECTION,
+	PROP_SPECIFIC_OBJECT,
+	PROP_DEVICES,
+	PROP_STATE,
+	PROP_DEFAULT,
+	PROP_DEFAULT6,
+	PROP_VPN,
+	PROP_VPN_STATE,
+	PROP_BANNER,
+
+	LAST_PROP
+};
+
+static void
+nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
+                                 NMVPNConnectionState vpn_state,
+                                 NMVPNConnectionStateReason reason)
+{
+	NMVPNConnectionPrivate *priv;
+	NMActiveConnectionState new_ac_state;
+	NMVPNConnectionState old_vpn_state;
+	char *ip_iface;
+
+	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	if (vpn_state == priv->vpn_state)
+		return;
+
+	old_vpn_state = priv->vpn_state;
+	priv->vpn_state = vpn_state;
+
+	/* Save ip_iface since when the VPN goes down it may get freed
+	 * before we're done with it.
+	 */
+	ip_iface = g_strdup (priv->tundev);
+
+	/* Set the NMActiveConnection state based on VPN state */
+	switch (vpn_state) {
+	case NM_VPN_CONNECTION_STATE_PREPARE:
+	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
+	case NM_VPN_CONNECTION_STATE_CONNECT:
+	case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
+		new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATING;
+		break;
+	case NM_VPN_CONNECTION_STATE_ACTIVATED:
+		new_ac_state = NM_ACTIVE_CONNECTION_STATE_ACTIVATED;
+		break;
+	default:
+		new_ac_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
+		break;
+	}
+
+	if (new_ac_state != priv->state) {
+		priv->state = new_ac_state;
+		g_object_notify (G_OBJECT (connection), NM_ACTIVE_CONNECTION_STATE);
+	}
+
+	/* The connection gets destroyed by the VPN manager when it enters the
+	 * disconnected/failed state, but we need to keep it around for a bit
+	 * to send out signals and handle the dispatcher.  So ref it.
+	 */
+	g_object_ref (connection);
+
+	g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason);
+	g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE);
+
+	/* Call dispatcher after the event gets processed internally */
+	switch (vpn_state) {
+	case NM_VPN_CONNECTION_STATE_ACTIVATED:
+		nm_utils_call_dispatcher ("vpn-up",
+		                          priv->connection,
+		                          priv->parent_dev,
+		                          ip_iface);
+		break;
+	case NM_VPN_CONNECTION_STATE_FAILED:
+	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
+		if (old_vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED) {
+			nm_utils_call_dispatcher ("vpn-down",
+			                          priv->connection,
+			                          priv->parent_dev,
+			                          ip_iface);
+		}
+		break;
+	default:
+		break;
+	}
+
+	g_free (ip_iface);
+	g_object_unref (connection);
+}
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+
+	if (new_state <= NM_DEVICE_STATE_DISCONNECTED) {
+		nm_vpn_connection_set_vpn_state (connection,
+		                                 NM_VPN_CONNECTION_STATE_DISCONNECTED,
+		                                 NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED);
+	} else if (new_state == NM_DEVICE_STATE_FAILED) {
+		nm_vpn_connection_set_vpn_state (connection,
+		                                 NM_VPN_CONNECTION_STATE_FAILED,
+		                                 NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED);
+	}
+}
+
+static void
+device_ip4_config_changed (NMDevice *device,
+                           GParamSpec *pspec,
+                           gpointer user_data)
+{
+	NMVPNConnection *vpn = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn);
+
+	if (   (priv->vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
+	    || !nm_device_get_ip4_config (device))
+		return;
+
+	if (priv->gw_route)
+		rtnl_route_put (priv->gw_route);
+
+	/* Re-add the VPN gateway route */
+	priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, priv->ip4_config);
+}
+
+NMVPNConnection *
+nm_vpn_connection_new (NMConnection *connection,
+                       NMActRequest *act_request,
+                       NMDevice *parent_device)
+{
+	NMVPNConnection *self;
+	NMVPNConnectionPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (act_request), NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL);
+
+	self = (NMVPNConnection *) g_object_new (NM_TYPE_VPN_CONNECTION, NULL);
+	if (!self)
+		return NULL;
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	priv->connection = g_object_ref (connection);
+	priv->parent_dev = g_object_ref (parent_device);
+	priv->act_request = g_object_ref (act_request);
+
+	priv->device_monitor = g_signal_connect (parent_device, "state-changed",
+									 G_CALLBACK (device_state_changed),
+									 self);
+
+	priv->device_ip4 = g_signal_connect (parent_device, "notify::" NM_DEVICE_INTERFACE_IP4_CONFIG,
+	                                     G_CALLBACK (device_ip4_config_changed),
+	                                     self);
+	return self;
+}
+
+static const char *
+nm_vpn_connection_get_service (NMVPNConnection *connection)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMSettingVPN *setting;
+
+	setting = (NMSettingVPN *) nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_VPN);
+	return nm_setting_vpn_get_service_type (setting);
+}
+
+static void
+plugin_failed (DBusGProxy *proxy,
+			   NMVPNPluginFailure plugin_failure,
+			   gpointer user_data)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (user_data);
+
+	nm_log_warn (LOGD_VPN, "VPN plugin failed: %d", plugin_failure);
+
+	switch (plugin_failure) {
+	case NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED:
+		priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED;
+		break;
+	case NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG:
+		priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID;
+		break;
+	default:
+		priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN;
+	}
+}
+
+static void
+plugin_state_changed (DBusGProxy *proxy,
+                      NMVPNServiceState state,
+                      gpointer user_data)
+{
+	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	nm_log_info (LOGD_VPN, "VPN plugin state changed: %d", state);
+
+	if (state == NM_VPN_SERVICE_STATE_STOPPED) {
+		/* Clear connection secrets to ensure secrets get requested each time the
+		 * connection is activated.
+		 */
+		nm_connection_clear_secrets (priv->connection);
+
+		switch (nm_vpn_connection_get_vpn_state (connection)) {
+		case NM_VPN_CONNECTION_STATE_PREPARE:
+		case NM_VPN_CONNECTION_STATE_NEED_AUTH:
+		case NM_VPN_CONNECTION_STATE_CONNECT:
+		case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
+		case NM_VPN_CONNECTION_STATE_ACTIVATED:
+			nm_log_info (LOGD_VPN, "VPN plugin state change reason: %d", priv->failure_reason);
+			nm_vpn_connection_set_vpn_state (connection,
+			                                 NM_VPN_CONNECTION_STATE_FAILED,
+											 priv->failure_reason);
+
+			/* Reset the failure reason */
+			priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static const char *
+ip_address_to_string (guint32 numeric)
+{
+	struct in_addr temp_addr;
+	static char buf[INET_ADDRSTRLEN + 1];
+
+	memset (&buf, '\0', sizeof (buf));
+	temp_addr.s_addr = numeric;
+
+	if (inet_ntop (AF_INET, &temp_addr, buf, INET_ADDRSTRLEN)) {
+		return buf;
+	} else {
+		nm_log_warn (LOGD_VPN, "error converting IP4 address 0x%X",
+		             ntohl (temp_addr.s_addr));
+		return NULL;
+	}
+}
+
+static void
+print_vpn_config (NMIP4Config *config,
+                  guint32 internal_gw,
+                  const char *tundev,
+                  const char *banner)
+{
+	NMIP4Address *addr;
+	char *dns_domain = NULL;
+	guint32 num, i;
+
+	g_return_if_fail (config != NULL);
+
+	addr = nm_ip4_config_get_address (config, 0);
+
+	nm_log_info (LOGD_VPN, "VPN Gateway: %s", ip_address_to_string (nm_ip4_address_get_gateway (addr)));
+	if (internal_gw)
+		nm_log_info (LOGD_VPN, "Internal Gateway: %s", ip_address_to_string (internal_gw));
+	nm_log_info (LOGD_VPN, "Tunnel Device: %s", tundev);
+	nm_log_info (LOGD_VPN, "Internal IP4 Address: %s", ip_address_to_string (nm_ip4_address_get_address (addr)));
+	nm_log_info (LOGD_VPN, "Internal IP4 Prefix: %d", nm_ip4_address_get_prefix (addr));
+	nm_log_info (LOGD_VPN, "Internal IP4 Point-to-Point Address: %s",
+	             ip_address_to_string (nm_ip4_config_get_ptp_address (config)));
+	nm_log_info (LOGD_VPN, "Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (config));
+
+	num = nm_ip4_config_get_num_routes (config);
+	for (i = 0; i < num; i++) {
+		NMIP4Route *route;
+
+		route = nm_ip4_config_get_route (config, i);
+		nm_log_info (LOGD_VPN, "Static Route: %s/%d   Next Hop: %s",
+		             ip_address_to_string (nm_ip4_route_get_dest (route)),
+		             nm_ip4_route_get_prefix (route),
+		             ip_address_to_string (nm_ip4_route_get_next_hop (route)));
+	}
+
+	num = nm_ip4_config_get_num_nameservers (config);
+	for (i = 0; i < num; i++) {
+		nm_log_info (LOGD_VPN, "Internal IP4 DNS: %s",
+		             ip_address_to_string (nm_ip4_config_get_nameserver (config, i)));
+	}
+
+	if (nm_ip4_config_get_num_domains (config) > 0)
+		dns_domain = (char *) nm_ip4_config_get_domain (config, 0);
+
+	nm_log_info (LOGD_VPN, "DNS Domain: '%s'", dns_domain ? dns_domain : "(none)");
+
+	if (banner && strlen (banner)) {
+		nm_log_info (LOGD_VPN, "Login Banner:");
+		nm_log_info (LOGD_VPN, "-----------------------------------------");
+		nm_log_info (LOGD_VPN, "%s", banner);
+		nm_log_info (LOGD_VPN, "-----------------------------------------");
+	}
+}
+
+static void
+nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
+                                  GHashTable *config_hash,
+                                  gpointer user_data)
+{
+	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMSettingIP4Config *s_ip4;
+	NMIP4Address *addr;
+	NMIP4Config *config;
+	GValue *val;
+	int i;
+	guint32 vpn_ext_gw = 0;
+
+	nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.",
+	             nm_vpn_connection_get_name (connection));
+
+	g_source_remove (priv->ipconfig_timeout);
+	priv->ipconfig_timeout = 0;
+
+	config = nm_ip4_config_new ();
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV);
+	if (val)
+		priv->tundev = g_strdup (g_value_get_string (val));
+	else {
+		nm_log_err (LOGD_VPN, "invalid or missing tunnel device received!");
+		goto error;
+	}
+
+	addr = nm_ip4_address_new ();
+	nm_ip4_address_set_prefix (addr, 24); /* default to class C */
+
+	/* Internal address of the VPN subnet's gateway */
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY);
+	if (val)
+		priv->ip4_internal_gw = g_value_get_uint (val);
+
+	/* External world-visible address of the VPN server */
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY);
+	if (val) {
+		nm_ip4_address_set_gateway (addr, g_value_get_uint (val));
+		vpn_ext_gw = g_value_get_uint (val);
+	}
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS);
+	if (val)
+		nm_ip4_address_set_address (addr, g_value_get_uint (val));
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PTP);
+	if (val)
+		nm_ip4_config_set_ptp_address (config, g_value_get_uint (val));
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX);
+	if (val)
+		nm_ip4_address_set_prefix (addr, g_value_get_uint (val));
+
+	if (nm_ip4_address_get_address (addr) && nm_ip4_address_get_prefix (addr)) {
+		nm_ip4_config_take_address (config, addr);
+	} else {
+		nm_log_err (LOGD_VPN, "invalid IP4 config received!");
+		nm_ip4_address_unref (addr);
+		goto error;
+	}
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DNS);
+	if (val) {
+		GArray *dns = (GArray *) g_value_get_boxed (val);
+
+		for (i = 0; i < dns->len; i++)
+			nm_ip4_config_add_nameserver (config, g_array_index (dns, guint, i));
+	}
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_NBNS);
+	if (val) {
+		GArray *nbns = (GArray *) g_value_get_boxed (val);
+
+		for (i = 0; i < nbns->len; i++)
+			nm_ip4_config_add_wins (config, g_array_index (nbns, guint, i));
+	}
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_MSS);
+	if (val)
+		nm_ip4_config_set_mss (config, g_value_get_uint (val));
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_MTU);
+	if (val)
+		nm_ip4_config_set_mtu (config, g_value_get_uint (val));
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN);
+	if (val)
+		nm_ip4_config_add_domain (config, g_value_get_string (val));
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_BANNER);
+	if (val) {
+		if (priv->banner)
+			g_free (priv->banner);
+		priv->banner = g_strdup (g_value_get_string (val));
+	}
+
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES);
+	if (val) {
+		GSList *routes;
+		GSList *iter;
+
+		routes = nm_utils_ip4_routes_from_gvalue (val);
+		for (iter = routes; iter; iter = iter->next) {
+			NMIP4Route *route = iter->data;
+
+			/* Ignore host routes to the VPN gateway since NM adds one itself
+			 * below.  Since NM knows more about the routing situation than
+			 * the VPN server, we want to use the NM created route instead of
+			 * whatever the server provides.
+			 */
+			if (   vpn_ext_gw
+			    && nm_ip4_route_get_dest (route) == vpn_ext_gw
+			    && nm_ip4_route_get_prefix (route) == 32)
+				continue;
+
+			/* Otherwise accept the VPN-provided route */
+			nm_ip4_config_take_route (config, route);
+		}
+
+		g_slist_free (routes);
+	}
+
+	print_vpn_config (config, priv->ip4_internal_gw, priv->tundev, priv->banner);
+
+	/* Merge in user overrides from the NMConnection's IPv4 setting */
+	s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_IP4_CONFIG));
+	nm_utils_merge_ip4_config (config, s_ip4);
+
+	nm_system_device_set_up_down_with_iface (priv->tundev, TRUE, NULL);
+
+	if (nm_system_apply_ip4_config (priv->tundev, config, 0, NM_IP4_COMPARE_FLAG_ALL)) {
+		NMNamedManager *named_mgr;
+
+		/* Add any explicit route to the VPN gateway through the parent device */
+		priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, config);
+
+		/* Add the VPN to DNS */
+		named_mgr = nm_named_manager_get ();
+		nm_named_manager_add_ip4_config (named_mgr, priv->tundev, config, NM_NAMED_IP_CONFIG_TYPE_VPN);
+		g_object_unref (named_mgr);
+
+		priv->ip4_config = config;
+
+		nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
+		             nm_vpn_connection_get_name (connection));
+		nm_vpn_connection_set_vpn_state (connection,
+		                                 NM_VPN_CONNECTION_STATE_ACTIVATED,
+		                                 NM_VPN_CONNECTION_STATE_REASON_NONE);
+		return;
+	}
+
+error:
+	nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.",
+	             nm_vpn_connection_get_name (connection));
+	nm_vpn_connection_set_vpn_state (connection,
+	                                 NM_VPN_CONNECTION_STATE_FAILED,
+	                                 NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID);
+	g_object_unref (config);
+}
+
+static gboolean
+nm_vpn_connection_ip_config_timeout (gpointer user_data)
+{
+	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	priv->ipconfig_timeout = 0;
+
+	/* If the activation request's state is still IP_CONFIG_GET and we're
+	 * in this timeout, cancel activation because it's taken too long.
+	 */
+	if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET) {
+		nm_log_warn (LOGD_VPN, "VPN connection '%s' (IP Config Get) timeout exceeded.",
+		             nm_vpn_connection_get_name (connection));
+		nm_vpn_connection_set_vpn_state (connection,
+		                                 NM_VPN_CONNECTION_STATE_FAILED,
+		                                 NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT);
+	}
+
+	return FALSE;
+}
+
+static void
+nm_vpn_connection_connect_cb (DBusGProxy *proxy, GError *err, gpointer user_data)
+{
+	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	nm_log_info (LOGD_VPN, "VPN connection '%s' (Connect) reply received.",
+	             nm_vpn_connection_get_name (connection));
+
+	if (err) {
+		nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.", 
+		             nm_vpn_connection_get_name (connection), err->message);
+		nm_vpn_connection_set_vpn_state (connection,
+		                                 NM_VPN_CONNECTION_STATE_FAILED,
+		                                 NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
+	} else {
+		nm_vpn_connection_set_vpn_state (connection,
+		                                 NM_VPN_CONNECTION_STATE_IP_CONFIG_GET,
+		                                 NM_VPN_CONNECTION_STATE_REASON_NONE);
+		
+		/* 40 second timeout waiting for IP config signal from VPN service */
+		priv->ipconfig_timeout = g_timeout_add_seconds (40, nm_vpn_connection_ip_config_timeout, connection);
+	}
+}
+
+static void
+really_activate (NMVPNConnection *connection)
+{
+	NMVPNConnectionPrivate *priv;
+
+	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+	g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_NEED_AUTH);
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	/* Ip4Config signal */
+	dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
+								G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->proxy, "Ip4Config",
+						DBUS_TYPE_G_MAP_OF_VARIANT,
+						G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "Ip4Config",
+						    G_CALLBACK (nm_vpn_connection_ip4_config_get),
+						    connection, NULL);
+
+	org_freedesktop_NetworkManager_VPN_Plugin_connect_async (priv->proxy,
+												  nm_connection_to_hash (priv->connection),
+												  nm_vpn_connection_connect_cb,
+												  connection);
+
+	nm_vpn_connection_set_vpn_state (connection,
+	                                 NM_VPN_CONNECTION_STATE_CONNECT,
+	                                 NM_VPN_CONNECTION_STATE_REASON_NONE);
+}
+
+void
+nm_vpn_connection_activate (NMVPNConnection *connection)
+{
+	NMVPNConnectionPrivate *priv;
+	NMDBusManager *dbus_mgr;
+
+	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+	g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_PREPARE);
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	dbus_mgr = nm_dbus_manager_get ();
+	priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (dbus_mgr),
+	                                         nm_vpn_connection_get_service (connection),
+	                                         NM_VPN_DBUS_PLUGIN_PATH,
+	                                         NM_VPN_DBUS_PLUGIN_INTERFACE);
+	g_object_unref (dbus_mgr);
+
+	dbus_g_proxy_add_signal (priv->proxy, "Failure", G_TYPE_UINT, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "Failure",
+								 G_CALLBACK (plugin_failed),
+								 connection, NULL);
+
+	/* StateChanged signal */
+	dbus_g_proxy_add_signal (priv->proxy, "StateChanged", G_TYPE_UINT, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->proxy, "StateChanged",
+	                             G_CALLBACK (plugin_state_changed),
+	                             connection, NULL);
+
+	nm_vpn_connection_set_vpn_state (connection,
+	                                 NM_VPN_CONNECTION_STATE_NEED_AUTH,
+	                                 NM_VPN_CONNECTION_STATE_REASON_NONE);
+}
+
+const char *
+nm_vpn_connection_get_active_connection_path (NMVPNConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ac_path;
+}
+
+const char *
+nm_vpn_connection_get_name (NMVPNConnection *connection)
+{
+	NMVPNConnectionPrivate *priv;
+	NMSettingConnection *setting;
+
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	setting = (NMSettingConnection *) nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_CONNECTION);
+
+	return nm_setting_connection_get_id (setting);
+}
+
+NMConnection *
+nm_vpn_connection_get_connection (NMVPNConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->connection;
+}
+
+NMVPNConnectionState
+nm_vpn_connection_get_vpn_state (NMVPNConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NM_VPN_CONNECTION_STATE_UNKNOWN);
+
+	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state;
+}
+
+const char *
+nm_vpn_connection_get_banner (NMVPNConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->banner;
+}
+
+NMIP4Config *
+nm_vpn_connection_get_ip4_config (NMVPNConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_config;
+}
+
+const char *
+nm_vpn_connection_get_ip_iface (NMVPNConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->tundev;
+}
+
+NMDevice *
+nm_vpn_connection_get_parent_device (NMVPNConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->parent_dev;
+}
+
+guint32
+nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), 0);
+
+	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_internal_gw;
+}
+
+void
+nm_vpn_connection_fail (NMVPNConnection *connection,
+                        NMVPNConnectionStateReason reason)
+{
+	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+
+	nm_vpn_connection_set_vpn_state (connection,
+	                                 NM_VPN_CONNECTION_STATE_FAILED,
+	                                 reason);
+}
+
+void
+nm_vpn_connection_disconnect (NMVPNConnection *connection,
+                              NMVPNConnectionStateReason reason)
+{
+	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+
+	nm_vpn_connection_set_vpn_state (connection,
+	                                 NM_VPN_CONNECTION_STATE_DISCONNECTED,
+	                                 reason);
+}
+
+/******************************************************************************/
+
+static gboolean
+secrets_update_setting (NMSecretsProviderInterface *interface,
+                        const char *setting_name,
+                        GHashTable *new)
+{
+	NMVPNConnection *self = NM_VPN_CONNECTION (interface);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	g_return_val_if_fail (priv->connection != NULL, FALSE);
+
+	if (strcmp (setting_name, NM_SETTING_VPN_SETTING_NAME))
+		return FALSE;
+
+	if (!nm_connection_update_secrets (priv->connection, NM_SETTING_VPN_SETTING_NAME, new, &error)) {
+		nm_log_warn (LOGD_VPN, "Failed to update VPN secrets: %d %s",
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(none)");
+		g_clear_error (&error);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+secrets_result (NMSecretsProviderInterface *interface,
+	            const char *setting_name,
+	            RequestSecretsCaller caller,
+	            const GSList *updated,
+	            GError *error)
+{
+	NMVPNConnection *self = NM_VPN_CONNECTION (interface);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->connection != NULL);
+	g_return_if_fail (caller == SECRETS_CALLER_VPN);
+
+	if (error)
+		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+	else
+		really_activate (self);
+}
+
+static void
+secrets_provider_interface_init (NMSecretsProviderInterface *sp_interface_class)
+{
+	/* interface implementation */
+	sp_interface_class->update_setting = secrets_update_setting;
+	sp_interface_class->result = secrets_result;
+}
+
+static void
+connection_need_secrets_cb  (DBusGProxy *proxy,
+                             char *setting_name,
+                             GError *error,
+                             gpointer user_data)
+{
+	NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	if (error) {
+		nm_log_err (LOGD_VPN, "NeedSecrets failed: %s %s",
+		            g_quark_to_string (error->domain),
+		            error->message);
+		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+		return;
+	}
+
+	if (!setting_name || !strlen (setting_name)) {
+		/* No secrets required */
+		really_activate (self);
+		return;
+	}
+
+	/* Get the secrets the VPN plugin wants */
+	if (!nm_secrets_provider_interface_get_secrets (NM_SECRETS_PROVIDER_INTERFACE (self),
+                                                    priv->connection,
+                                                    setting_name,
+                                                    FALSE,
+                                                    SECRETS_CALLER_VPN,
+                                                    NULL,
+                                                    NULL))
+		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+}
+
+static void
+call_need_secrets (NMVPNConnection *vpn_connection)
+{
+	NMVPNConnectionPrivate *priv;
+	GHashTable *settings;
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn_connection);
+	settings = nm_connection_to_hash (priv->connection);
+	org_freedesktop_NetworkManager_VPN_Plugin_need_secrets_async (priv->proxy,
+	                         settings,
+	                         connection_need_secrets_cb,
+	                         vpn_connection);
+	g_hash_table_destroy (settings);
+}
+
+static void
+vpn_cleanup (NMVPNConnection *connection)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	if (priv->tundev) {
+		nm_system_device_set_up_down_with_iface (priv->tundev, FALSE, NULL);
+		/* FIXME: use AF_UNSPEC here when we have IPv6 support */
+		nm_system_device_flush_routes_with_iface (priv->tundev, AF_INET);
+		nm_system_device_flush_addresses_with_iface (priv->tundev);
+	}
+
+	if (priv->ip4_config) {
+		NMIP4Config *parent_config;
+		NMNamedManager *named_mgr;
+
+		/* Remove attributes of the VPN's IP4 Config */
+		named_mgr = nm_named_manager_get ();
+		nm_named_manager_remove_ip4_config (named_mgr, priv->tundev, priv->ip4_config);
+		g_object_unref (named_mgr);
+
+		/* Remove any previously added VPN gateway host route */
+		if (priv->gw_route)
+			rtnl_route_del (nm_netlink_get_default_handle (), priv->gw_route, 0);
+
+		/* Reset routes and addresses of the currently active device */
+		parent_config = nm_device_get_ip4_config (priv->parent_dev);
+		if (parent_config) {
+			if (!nm_system_apply_ip4_config (nm_device_get_ip_iface (priv->parent_dev),
+			                                 nm_device_get_ip4_config (priv->parent_dev),
+			                                 nm_device_get_priority (priv->parent_dev),
+			                                 NM_IP4_COMPARE_FLAG_ADDRESSES | NM_IP4_COMPARE_FLAG_ROUTES)) {
+				nm_log_err (LOGD_VPN, "failed to re-apply VPN parent device addresses and routes.");
+			}
+		}
+	}
+
+	if (priv->gw_route) {
+		rtnl_route_put (priv->gw_route);
+		priv->gw_route = NULL;
+	}
+
+	if (priv->banner) {
+		g_free (priv->banner);
+		priv->banner = NULL;
+	}
+
+	if (priv->tundev) {
+		g_free (priv->tundev);
+		priv->tundev = NULL;
+	}
+
+	/* Clear out connection secrets to ensure that the settings service
+	 * gets asked for them next time the connection is activated.
+	 */
+	if (priv->connection)
+		nm_connection_clear_secrets (priv->connection);
+}
+
+static void
+connection_state_changed (NMVPNConnection *connection,
+                          NMVPNConnectionState state,
+                          NMVPNConnectionStateReason reason)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	nm_secrets_provider_interface_cancel_get_secrets (NM_SECRETS_PROVIDER_INTERFACE (priv->act_request));
+
+	switch (state) {
+	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
+		call_need_secrets (connection);
+		break;
+	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
+	case NM_VPN_CONNECTION_STATE_FAILED:
+		if (priv->proxy) {
+			GError *err = NULL;
+
+			org_freedesktop_NetworkManager_VPN_Plugin_disconnect (priv->proxy, &err);
+			if (err) {
+				nm_log_warn (LOGD_VPN, "error disconnecting VPN: %s", err->message);
+				g_error_free (err);
+			}
+
+			g_object_unref (priv->proxy);
+			priv->proxy = NULL;
+		}
+		vpn_cleanup (connection);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+nm_vpn_connection_init (NMVPNConnection *connection)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMDBusManager *dbus_mgr;
+
+	priv->state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
+	priv->vpn_state = NM_VPN_CONNECTION_STATE_PREPARE;
+	priv->ac_path = nm_active_connection_get_next_object_path ();
+
+	dbus_mgr = nm_dbus_manager_get ();
+	dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (dbus_mgr),
+								  priv->ac_path,
+								  G_OBJECT (connection));
+	g_object_unref (dbus_mgr);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
+
+	if (priv->disposed) {
+		G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
+		return;
+	}
+	priv->disposed = TRUE;
+
+	if (priv->gw_route)
+		rtnl_route_put (priv->gw_route);
+
+	if (priv->device_ip4)
+		g_signal_handler_disconnect (priv->parent_dev, priv->device_ip4);
+
+	if (priv->device_monitor)
+		g_signal_handler_disconnect (priv->parent_dev, priv->device_monitor);
+
+	g_object_unref (priv->parent_dev);
+
+	if (priv->ip4_config)
+		g_object_unref (priv->ip4_config);
+
+	if (priv->ipconfig_timeout)
+		g_source_remove (priv->ipconfig_timeout);
+
+	if (priv->proxy)
+		g_object_unref (priv->proxy);
+
+	g_object_unref (priv->act_request);
+	g_object_unref (priv->connection);
+
+	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
+
+	g_free (priv->banner);
+	g_free (priv->tundev);
+	g_free (priv->ac_path);
+
+	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_SERVICE_NAME:
+		nm_active_connection_scope_to_value (priv->connection, value);
+		break;
+	case PROP_CONNECTION:
+		g_value_set_boxed (value, nm_connection_get_path (priv->connection));
+		break;
+	case PROP_SPECIFIC_OBJECT:
+		g_value_set_boxed (value, nm_act_request_get_active_connection_path (priv->act_request));
+		break;
+	case PROP_DEVICES:
+		g_value_take_boxed (value, g_ptr_array_new ());
+		break;
+	case PROP_STATE:
+		g_value_set_uint (value, priv->state);
+		break;
+	case PROP_DEFAULT:
+		g_value_set_boolean (value, priv->is_default);
+		break;
+	case PROP_DEFAULT6:
+		g_value_set_boolean (value, priv->is_default6);
+		break;
+	case PROP_VPN:
+		g_value_set_boolean (value, TRUE);
+		break;
+	case PROP_VPN_STATE:
+		g_value_set_uint (value, priv->vpn_state);
+		break;
+	case PROP_BANNER:
+		g_value_set_string (value, priv->banner ? priv->banner : "");
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (connection_class);
+
+	g_type_class_add_private (connection_class, sizeof (NMVPNConnectionPrivate));
+
+	/* virtual methods */
+	connection_class->vpn_state_changed = connection_state_changed;
+	object_class->get_property = get_property;
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_SERVICE_NAME,
+		 g_param_spec_string (NM_ACTIVE_CONNECTION_SERVICE_NAME,
+							  "Service name",
+							  "Service name",
+							  NULL,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_CONNECTION,
+		 g_param_spec_boxed (NM_ACTIVE_CONNECTION_CONNECTION,
+							  "Connection",
+							  "Connection",
+							  DBUS_TYPE_G_OBJECT_PATH,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_SPECIFIC_OBJECT,
+		 g_param_spec_boxed (NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT,
+							  "Specific object",
+							  "Specific object",
+							  DBUS_TYPE_G_OBJECT_PATH,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_DEVICES,
+		 g_param_spec_boxed (NM_ACTIVE_CONNECTION_DEVICES,
+							  "Devices",
+							  "Devices",
+							  DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_STATE,
+		 g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE,
+							  "State",
+							  "State",
+							  NM_ACTIVE_CONNECTION_STATE_UNKNOWN,
+							  NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+							  NM_ACTIVE_CONNECTION_STATE_UNKNOWN,
+							  G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_DEFAULT,
+		 g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT,
+							   "Default",
+							   "Is the default IPv4 active connection",
+							   FALSE,
+							   G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_DEFAULT6,
+		 g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6,
+							   "Default6",
+							   "Is the default IPv6 active connection",
+							   FALSE,
+							   G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_VPN,
+		 g_param_spec_boolean (NM_ACTIVE_CONNECTION_VPN,
+							   "VPN",
+							   "Is a VPN connection",
+							   TRUE,
+							   G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_VPN_STATE,
+		 g_param_spec_uint (NM_VPN_CONNECTION_VPN_STATE,
+						"VpnState",
+						"Current VPN state",
+						NM_VPN_CONNECTION_STATE_UNKNOWN,
+						NM_VPN_CONNECTION_STATE_DISCONNECTED,
+						NM_VPN_CONNECTION_STATE_UNKNOWN,
+						G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_BANNER,
+		 g_param_spec_string (NM_VPN_CONNECTION_BANNER,
+						  "Banner",
+						  "Login Banner",
+						  NULL,
+						  G_PARAM_READABLE));
+
+	/* signals */
+	signals[VPN_STATE_CHANGED] =
+		g_signal_new ("vpn-state-changed",
+				    G_OBJECT_CLASS_TYPE (object_class),
+				    G_SIGNAL_RUN_FIRST,
+				    G_STRUCT_OFFSET (NMVPNConnectionClass, vpn_state_changed),
+				    NULL, NULL,
+				    _nm_marshal_VOID__UINT_UINT,
+				    G_TYPE_NONE, 2,
+				    G_TYPE_UINT, G_TYPE_UINT);
+
+	signals[PROPERTIES_CHANGED] = 
+		nm_properties_changed_signal_new (object_class,
+								    G_STRUCT_OFFSET (NMVPNConnectionClass, properties_changed));
+
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (object_class),
+									 &dbus_glib_nm_vpn_connection_object_info);
+}
+
diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h
new file mode 100644
index 00000000..d69674db
--- /dev/null
+++ b/src/vpn-manager/nm-vpn-connection.h
@@ -0,0 +1,78 @@
+/* -*- 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_VPN_CONNECTION_H
+#define NM_VPN_CONNECTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "NetworkManagerVPN.h"
+#include "nm-device.h"
+#include "nm-activation-request.h"
+#include "nm-secrets-provider-interface.h"
+
+#define NM_TYPE_VPN_CONNECTION            (nm_vpn_connection_get_type ())
+#define NM_VPN_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnection))
+#define NM_VPN_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass))
+#define NM_IS_VPN_CONNECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_CONNECTION))
+#define NM_IS_VPN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_CONNECTION))
+#define NM_VPN_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass))
+
+#define NM_VPN_CONNECTION_VPN_STATE "vpn-state"
+#define NM_VPN_CONNECTION_BANNER "banner"
+
+typedef struct {
+	GObject parent;
+} NMVPNConnection;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*vpn_state_changed) (NMVPNConnection *connection,
+	                           NMVPNConnectionState state,
+	                           NMVPNConnectionStateReason reason);
+
+	void (*properties_changed) (NMVPNConnection *connection, GHashTable *properties);
+} NMVPNConnectionClass;
+
+GType nm_vpn_connection_get_type (void);
+
+NMVPNConnection * nm_vpn_connection_new (NMConnection *connection,
+                                         NMActRequest *act_request,
+                                         NMDevice *parent_device);
+
+void                 nm_vpn_connection_activate        (NMVPNConnection *connection);
+NMConnection *       nm_vpn_connection_get_connection  (NMVPNConnection *connection);
+const char *         nm_vpn_connection_get_active_connection_path (NMVPNConnection *connection);
+const char *         nm_vpn_connection_get_name        (NMVPNConnection *connection);
+NMVPNConnectionState nm_vpn_connection_get_vpn_state   (NMVPNConnection *connection);
+const char *         nm_vpn_connection_get_banner      (NMVPNConnection *connection);
+void                 nm_vpn_connection_fail            (NMVPNConnection *connection,
+                                                        NMVPNConnectionStateReason reason);
+void                 nm_vpn_connection_disconnect      (NMVPNConnection *connection,
+                                                        NMVPNConnectionStateReason reason);
+NMIP4Config *        nm_vpn_connection_get_ip4_config  (NMVPNConnection *connection);
+const char *         nm_vpn_connection_get_ip_iface    (NMVPNConnection *connection);
+NMDevice *           nm_vpn_connection_get_parent_device (NMVPNConnection *connection);
+guint32              nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection);
+
+#endif /* NM_VPN_CONNECTION_H */
diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c
new file mode 100644
index 00000000..221a8b54
--- /dev/null
+++ b/src/vpn-manager/nm-vpn-manager.c
@@ -0,0 +1,369 @@
+/* -*- 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-vpn-manager.h"
+#include "nm-vpn-service.h"
+#include "nm-vpn-connection.h"
+#include "nm-setting-vpn.h"
+#include "nm-dbus-manager.h"
+#include "NetworkManagerVPN.h"
+#include "nm-marshal.h"
+
+G_DEFINE_TYPE (NMVPNManager, nm_vpn_manager, G_TYPE_OBJECT)
+
+#define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVPNManagerPrivate))
+
+typedef struct {
+	GSList *services;
+} NMVPNManagerPrivate;
+
+enum {
+	CONNECTION_ACTIVATED,
+	CONNECTION_DEACTIVATED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+GQuark
+nm_vpn_manager_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-vpn-manager-error");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_vpn_manager_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* The base device for the VPN connection is not active. */
+			ENUM_ENTRY (NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE, "BaseDeviceNotActive"),
+			/* The requested VPN connection was invalid. */
+			ENUM_ENTRY (NM_VPN_MANAGER_ERROR_CONNECTION_INVALID, "ConnectionInvalid"),
+			/* The VPN service required by this VPN connection did not exist or was invalid. */
+			ENUM_ENTRY (NM_VPN_MANAGER_ERROR_SERVICE_INVALID, "ServiceInvalid"),
+			/* The VPN service required by this VPN connection could not be started. */
+			ENUM_ENTRY (NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED, "ServiceStartFailed"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMVPNManagerError", values);
+	}
+	return etype;
+}
+
+
+
+static NMVPNService *
+nm_vpn_manager_get_service (NMVPNManager *manager, const char *service_name)
+{
+	GSList *iter;
+
+	for (iter = NM_VPN_MANAGER_GET_PRIVATE (manager)->services; iter; iter = iter->next) {
+		NMVPNService *service = NM_VPN_SERVICE (iter->data);
+
+		if (!strcmp (service_name, nm_vpn_service_get_name (service)))
+			return g_object_ref (service);
+	}
+
+	return NULL;
+}
+
+static void
+remove_service (gpointer data, GObject *service)
+{
+	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (data);
+
+	priv->services = g_slist_remove (priv->services, service);
+}
+
+static void
+nm_vpn_manager_add_service (NMVPNManager *manager, NMVPNService *service)
+{
+	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
+
+	priv->services = g_slist_prepend (priv->services, service);
+	g_object_weak_ref (G_OBJECT (service), remove_service, manager);
+}
+
+static NMVPNConnection *
+find_active_vpn_connection_by_connection (NMVPNManager *manager, NMConnection *connection)
+{
+	NMVPNManagerPrivate *priv;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+	priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
+	for (iter = priv->services; iter; iter = g_slist_next (iter)) {
+		GSList *connections, *elt;
+
+		connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+		for (elt = connections; elt; elt = g_slist_next (elt)) {
+			NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
+
+			if (nm_vpn_connection_get_connection (vpn) == connection)
+				return vpn;
+		}
+	}
+	return NULL;
+}
+
+static void
+connection_vpn_state_changed (NMVPNConnection *connection,
+                              NMVPNConnectionState state,
+                              NMVPNConnectionStateReason reason,
+                              gpointer user_data)
+{
+	NMVPNManager *manager = NM_VPN_MANAGER (user_data);
+
+	switch (state) {
+	case NM_VPN_CONNECTION_STATE_ACTIVATED:
+		g_signal_emit (manager, signals[CONNECTION_ACTIVATED], 0, connection);
+		break;
+	case NM_VPN_CONNECTION_STATE_FAILED:
+	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
+		g_signal_emit (manager, signals[CONNECTION_DEACTIVATED], 0, connection, state, reason);
+		break;
+	default:
+		break;
+	}
+}
+
+NMVPNConnection *
+nm_vpn_manager_activate_connection (NMVPNManager *manager,
+                                    NMConnection *connection,
+                                    NMActRequest *act_request,
+                                    NMDevice *device,
+                                    GError **error)
+{
+	NMSettingVPN *vpn_setting;
+	NMVPNService *service;
+	NMVPNConnection *vpn = NULL;
+	const char *service_type;
+
+	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (act_request), NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+	g_return_val_if_fail (error != NULL, NULL);
+	g_return_val_if_fail (*error == NULL, NULL);
+
+	if (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED) {
+		g_set_error (error,
+		             NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE,
+		             "%s", "The base device for the VPN connection was not active.");
+		return NULL;
+	}
+
+	vpn_setting = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
+	if (!vpn_setting) {
+		g_set_error (error,
+		             NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_CONNECTION_INVALID,
+		             "%s", "The connection was not a VPN connection.");
+		return NULL;
+	}
+
+	vpn = find_active_vpn_connection_by_connection (manager, connection);
+	if (vpn) {
+		nm_vpn_connection_disconnect (vpn, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
+		vpn = NULL;
+	}
+
+	service_type = nm_setting_vpn_get_service_type (vpn_setting);
+	service = nm_vpn_manager_get_service (manager, service_type);
+	if (!service) {
+		service = nm_vpn_service_new (service_type);
+		if (service)
+			nm_vpn_manager_add_service (manager, service);
+	}
+
+	if (service) {
+		vpn = nm_vpn_service_activate (service, connection, act_request, device, error);
+		if (vpn) {
+			g_signal_connect (vpn, "vpn-state-changed",
+			                  G_CALLBACK (connection_vpn_state_changed),
+			                  manager);
+		}
+	} else {
+		g_set_error (error,
+		             NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID,
+		             "%s", "The VPN service was invalid.");
+	}
+
+	return vpn;
+}
+
+gboolean
+nm_vpn_manager_deactivate_connection (NMVPNManager *manager,
+                                      const char *path,
+                                      NMVPNConnectionStateReason reason)
+{
+	NMVPNManagerPrivate *priv;
+	GSList *iter;
+	gboolean found = FALSE;
+
+	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), FALSE);
+	g_return_val_if_fail (path != NULL, FALSE);
+
+	priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
+	for (iter = priv->services; iter; iter = g_slist_next (iter)) {
+		GSList *connections, *elt;
+
+		connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+		for (elt = connections; elt; elt = g_slist_next (elt)) {
+			NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
+			const char *vpn_path;
+
+			vpn_path = nm_vpn_connection_get_active_connection_path (vpn);
+			if (!strcmp (path, vpn_path)) {
+				nm_vpn_connection_disconnect (vpn, reason);
+				found = TRUE;
+			}
+		}
+	}
+
+	return found ? TRUE : FALSE;
+}
+
+void
+nm_vpn_manager_add_active_connections (NMVPNManager *manager,
+                                       NMConnection *filter,
+                                       GPtrArray *array)
+{
+	NMVPNManagerPrivate *priv;
+	GSList *iter;
+
+	g_return_if_fail (NM_IS_VPN_MANAGER (manager));
+	g_return_if_fail (array != NULL);
+
+	priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
+	for (iter = priv->services; iter; iter = g_slist_next (iter)) {
+		GSList *active, *elt;
+
+		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+		for (elt = active; elt; elt = g_slist_next (elt)) {
+			NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
+			const char *path;
+
+			if (!filter || (nm_vpn_connection_get_connection (vpn) == filter)) {
+				path = nm_vpn_connection_get_active_connection_path (vpn);
+				g_ptr_array_add (array, g_strdup (path));
+			}
+		}
+	}
+}
+
+GSList *
+nm_vpn_manager_get_active_connections (NMVPNManager *manager)
+{
+	NMVPNManagerPrivate *priv;
+	GSList *iter;
+	GSList *list = NULL;
+
+	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
+
+	priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
+	for (iter = priv->services; iter; iter = g_slist_next (iter)) {
+		GSList *active, *elt;
+
+		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+		for (elt = active; elt; elt = g_slist_next (elt))
+			list = g_slist_append (list, g_object_ref (NM_VPN_CONNECTION (elt->data)));
+	}
+
+	return list;
+}
+
+NMVPNManager *
+nm_vpn_manager_get (void)
+{
+	static NMVPNManager *singleton = NULL;
+
+	if (!singleton)
+		singleton = NM_VPN_MANAGER (g_object_new (NM_TYPE_VPN_MANAGER, NULL));
+	else
+		g_object_ref (singleton);
+
+	g_assert (singleton);
+	return singleton;
+}
+
+/******************************************************************************/
+
+static void
+nm_vpn_manager_init (NMVPNManager *manager)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
+
+	g_slist_foreach (priv->services, (GFunc) g_object_unref, NULL);
+
+	G_OBJECT_CLASS (nm_vpn_manager_parent_class)->finalize (object);
+}
+
+static void
+nm_vpn_manager_class_init (NMVPNManagerClass *manager_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+	g_type_class_add_private (manager_class, sizeof (NMVPNManagerPrivate));
+
+	/* virtual methods */
+	object_class->finalize = finalize;
+
+	/* signals */
+	signals[CONNECTION_ACTIVATED] =
+		g_signal_new ("connection-activated",
+				    G_OBJECT_CLASS_TYPE (object_class),
+				    G_SIGNAL_RUN_FIRST,
+				    0, NULL, NULL,
+				    g_cclosure_marshal_VOID__OBJECT,
+				    G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+	signals[CONNECTION_DEACTIVATED] =
+		g_signal_new ("connection-deactivated",
+				    G_OBJECT_CLASS_TYPE (object_class),
+				    G_SIGNAL_RUN_FIRST,
+				    G_STRUCT_OFFSET (NMVPNManagerClass, connection_deactivated),
+				    NULL, NULL,
+				    _nm_marshal_VOID__OBJECT_UINT_UINT,
+				    G_TYPE_NONE, 3,
+				    G_TYPE_OBJECT, G_TYPE_UINT, G_TYPE_UINT);
+
+	dbus_g_error_domain_register (NM_VPN_MANAGER_ERROR, NULL, NM_TYPE_VPN_MANAGER_ERROR);
+}
+
diff --git a/src/vpn-manager/nm-vpn-manager.h b/src/vpn-manager/nm-vpn-manager.h
new file mode 100644
index 00000000..d07aa250
--- /dev/null
+++ b/src/vpn-manager/nm-vpn-manager.h
@@ -0,0 +1,86 @@
+/* -*- 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_VPN_MANAGER_H
+#define NM_VPN_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "nm-vpn-connection.h"
+#include "nm-activation-request.h"
+
+#define NM_TYPE_VPN_MANAGER            (nm_vpn_manager_get_type ())
+#define NM_VPN_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_MANAGER, NMVPNManager))
+#define NM_VPN_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_MANAGER, NMVPNManagerClass))
+#define NM_IS_VPN_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_MANAGER))
+#define NM_IS_VPN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_MANAGER))
+#define NM_VPN_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_MANAGER, NMVPNManagerClass))
+
+typedef enum
+{
+	NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE = 0,
+	NM_VPN_MANAGER_ERROR_CONNECTION_INVALID,
+	NM_VPN_MANAGER_ERROR_SERVICE_INVALID,
+	NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED,
+} NMVPNManagerError;
+
+#define NM_VPN_MANAGER_ERROR (nm_vpn_manager_error_quark ())
+#define NM_TYPE_VPN_MANAGER_ERROR (nm_vpn_manager_error_get_type ()) 
+
+GQuark nm_vpn_manager_error_quark (void);
+GType nm_vpn_manager_error_get_type (void);
+
+
+typedef struct {
+	GObject parent;
+} NMVPNManager;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*connection_deactivated) (NMVPNManager *manager,
+	                                NMVPNConnection *connection,
+	                                NMVPNConnectionState state,
+	                                NMVPNConnectionStateReason reason);
+} NMVPNManagerClass;
+
+GType nm_vpn_manager_get_type (void);
+
+NMVPNManager *nm_vpn_manager_get (void);
+
+NMVPNConnection *nm_vpn_manager_activate_connection (NMVPNManager *manager,
+                                                     NMConnection *connection,
+                                                     NMActRequest *act_request,
+                                                     NMDevice *device,
+                                                     GError **error);
+
+gboolean nm_vpn_manager_deactivate_connection (NMVPNManager *manager,
+                                               const char *path,
+                                               NMVPNConnectionStateReason reason);
+
+void nm_vpn_manager_add_active_connections (NMVPNManager *manager,
+                                            NMConnection *filter,
+                                            GPtrArray *list);
+
+GSList *nm_vpn_manager_get_active_connections (NMVPNManager *manager);
+
+#endif /* NM_VPN_VPN_MANAGER_H */
diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c
new file mode 100644
index 00000000..bdbb3774
--- /dev/null
+++ b/src/vpn-manager/nm-vpn-service.c
@@ -0,0 +1,478 @@
+/* -*- 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <dbus/dbus.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "nm-vpn-service.h"
+#include "nm-dbus-manager.h"
+#include "nm-logging.h"
+#include "nm-vpn-manager.h"
+#include "nm-glib-compat.h"
+
+G_DEFINE_TYPE (NMVPNService, nm_vpn_service, G_TYPE_OBJECT)
+
+typedef struct {
+	NMDBusManager *dbus_mgr;
+	char *name;
+	char *dbus_service;
+	char *program;
+
+	GPid pid;
+	GSList *connections;
+	guint service_start_timeout;
+	guint service_child_watch;
+	gulong name_owner_id;
+} NMVPNServicePrivate;
+
+#define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVPNServicePrivate))
+
+#define VPN_CONNECTION_GROUP "VPN Connection"
+
+static GKeyFile *
+find_service_file (const char *name)
+{
+	GDir *dir;
+	const char *fn;
+	GKeyFile *key_file = NULL;
+
+	dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
+	if (!dir)
+		return NULL;
+
+	while ((fn = g_dir_read_name (dir))) {
+		char *path;
+		gboolean found = FALSE;
+
+		/* only parse filenames that end with .name */
+		if (!g_str_has_suffix (fn, ".name"))
+			continue;
+
+		key_file = g_key_file_new ();
+		path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL);
+
+		if (g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL)) {
+			gchar *val;
+
+			val = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL);
+			if (val) {
+				if (!strcmp (val, name))
+					found = TRUE;
+				g_free (val);
+			}
+		}
+
+		g_free (path);
+
+		if (found)
+			break;
+
+		g_key_file_free (key_file);
+		key_file = NULL;
+	}
+
+	g_dir_close (dir);
+
+	return key_file;
+}
+
+NMVPNService *
+nm_vpn_service_new (const char *name)
+{
+	GKeyFile *key_file;
+	NMVPNService *service = NULL;
+	NMVPNServicePrivate *priv;
+	char *dbus_service = NULL;
+	char *program = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	key_file = find_service_file (name);
+	if (!key_file)
+		return NULL;
+
+	dbus_service = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL);
+	if (!dbus_service)
+		goto out;
+
+	program = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "program", NULL);
+	if (!program)
+		goto out;
+
+	service = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
+	if (!service)
+		goto out;
+
+	priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+
+	priv->name = g_strdup (name);
+	priv->dbus_service = dbus_service;
+	priv->program = program;
+
+	success = TRUE;
+
+ out:
+	g_key_file_free (key_file);
+
+	if (!success) {
+		g_free (dbus_service);
+		g_free (program);
+	}
+
+	return service;
+}
+
+const char *
+nm_vpn_service_get_name (NMVPNService *service)
+{
+	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
+
+	return NM_VPN_SERVICE_GET_PRIVATE (service)->name;
+}
+
+static void
+nm_vpn_service_connections_stop (NMVPNService *service,
+                                 gboolean fail,
+                                 NMVPNConnectionStateReason reason)
+{
+	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+	GSList *iter, *copy;
+
+	/* Copy because stopping the connection may remove it from the list
+	 * in the the NMVPNService objects' VPN connection state handler.
+	 */
+	copy = g_slist_copy (priv->connections);
+	for (iter = copy; iter; iter = iter->next) {
+		if (fail)
+			nm_vpn_connection_fail (NM_VPN_CONNECTION (iter->data), reason);
+		else
+			nm_vpn_connection_disconnect (NM_VPN_CONNECTION (iter->data), reason);
+	}
+	g_slist_free (copy);
+}
+
+/*
+ * nm_vpn_service_child_setup
+ *
+ * Set the process group ID of the newly forked process
+ *
+ */
+static void
+nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+	/* We are in the child process at this point */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
+}
+
+static void
+vpn_service_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+	NMVPNService *service = NM_VPN_SERVICE (user_data);
+	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+
+	if (WIFEXITED (status)) {
+		guint err = WEXITSTATUS (status);
+
+		if (err != 0) {
+			nm_log_warn (LOGD_VPN, "VPN service '%s' exited with error: %d",
+			             nm_vpn_service_get_name (service), WSTOPSIG (status));
+		}
+	} else if (WIFSTOPPED (status)) {
+		nm_log_warn (LOGD_VPN, "VPN service '%s' stopped unexpectedly with signal %d",
+		             nm_vpn_service_get_name (service), WSTOPSIG (status));
+	} else if (WIFSIGNALED (status)) {
+		nm_log_warn (LOGD_VPN, "VPN service '%s' died with signal %d",
+		             nm_vpn_service_get_name (service), WTERMSIG (status));
+	} else {
+		nm_log_warn (LOGD_VPN, "VPN service '%s' died from an unknown cause", 
+		             nm_vpn_service_get_name (service));
+	}
+
+	priv->pid = 0;
+	priv->service_child_watch = 0;
+
+	nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
+}
+
+static gboolean
+nm_vpn_service_timeout (gpointer data)
+{
+	NMVPNService *service = NM_VPN_SERVICE (data);
+
+	nm_log_warn (LOGD_VPN, "VPN service '%s' did not start in time, cancelling connections",
+	             nm_vpn_service_get_name (service));
+
+	NM_VPN_SERVICE_GET_PRIVATE (service)->service_start_timeout = 0;
+	nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
+
+	return FALSE;
+}
+
+static gboolean
+nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
+{
+	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+	char *vpn_argv[2];
+	gboolean success = FALSE;
+	GError *spawn_error = NULL;
+
+	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE);
+	g_return_val_if_fail (error != NULL, FALSE);
+	g_return_val_if_fail (*error == NULL, FALSE);
+
+	vpn_argv[0] = priv->program;
+	vpn_argv[1] = NULL;
+
+	success = g_spawn_async (NULL, vpn_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+	                         nm_vpn_service_child_setup, NULL, &priv->pid,
+	                         &spawn_error);
+	if (success) {
+		nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d", 
+		             nm_vpn_service_get_name (service), priv->dbus_service, priv->pid);
+
+		priv->service_child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service);
+		priv->service_start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service);
+	} else {
+		nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.",
+		             nm_vpn_service_get_name (service), spawn_error->code, spawn_error->message);
+
+		g_set_error (error,
+		             NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED,
+		             "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error");
+
+		nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
+		if (spawn_error)
+			g_error_free (spawn_error);
+	}
+
+	return success;
+}
+
+static gboolean
+destroy_service (gpointer data)
+{
+	g_object_unref (data);
+
+	return FALSE;
+}
+
+static void
+connection_vpn_state_changed (NMVPNConnection *connection,
+                              NMVPNConnectionState state,
+                              NMVPNConnectionStateReason reason,
+                              gpointer user_data)
+{
+	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data);
+
+	switch (state) {
+	case NM_VPN_CONNECTION_STATE_FAILED:
+	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
+		/* Remove the connection from our list */
+		priv->connections = g_slist_remove (priv->connections, connection);
+		g_object_unref (connection);
+
+		if (priv->connections == NULL) {
+			/* schedule a timeout (10 seconds) to destroy the service */
+			g_timeout_add_seconds (10, destroy_service, user_data);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+NMVPNConnection *
+nm_vpn_service_activate (NMVPNService *service,
+                         NMConnection *connection,
+                         NMActRequest *act_request,
+                         NMDevice *device,
+                         GError **error)
+{
+	NMVPNConnection *vpn;
+	NMVPNServicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (act_request), NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+	g_return_val_if_fail (error != NULL, NULL);
+	g_return_val_if_fail (*error == NULL, NULL);
+
+	priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+
+	vpn = nm_vpn_connection_new (connection, act_request, device);
+	g_signal_connect (vpn, "vpn-state-changed",
+				   G_CALLBACK (connection_vpn_state_changed),
+				   service);
+
+	priv->connections = g_slist_prepend (priv->connections, vpn);
+
+	if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service)) {
+		// FIXME: fill in error when errors happen
+		nm_vpn_connection_activate (vpn);
+	} else if (priv->service_start_timeout == 0) {
+		nm_log_info (LOGD_VPN, "Starting VPN service '%s'...",
+		             nm_vpn_service_get_name (service));
+		if (!nm_vpn_service_daemon_exec (service, error))
+			vpn = NULL;
+	}
+
+	return vpn;
+}
+
+GSList *
+nm_vpn_service_get_active_connections (NMVPNService *service)
+{
+	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
+
+	return g_slist_copy (NM_VPN_SERVICE_GET_PRIVATE (service)->connections);
+}
+
+static void
+nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
+							const char *name,
+							const char *old,
+							const char *new,
+							gpointer user_data)
+{
+	NMVPNService *service = NM_VPN_SERVICE (user_data);
+	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+	gboolean old_owner_good;
+	gboolean new_owner_good;
+
+	if (strcmp (name, priv->dbus_service))
+		return;
+
+	/* Service changed, no need to wait for the timeout any longer */
+	if (priv->service_start_timeout) {
+		g_source_remove (priv->service_start_timeout);
+		priv->service_start_timeout = 0;
+	}
+
+	old_owner_good = (old && (strlen (old) > 0));
+	new_owner_good = (new && (strlen (new) > 0));
+
+	if (!old_owner_good && new_owner_good) {
+		/* service just appeared */
+		GSList *iter;
+
+		nm_log_info (LOGD_VPN, "VPN service '%s' appeared, activating connections",
+		             nm_vpn_service_get_name (service));
+
+		for (iter = priv->connections; iter; iter = iter->next)
+			nm_vpn_connection_activate (NM_VPN_CONNECTION (iter->data));
+
+	} else if (old_owner_good && !new_owner_good) {
+		/* service went away */
+		nm_log_info (LOGD_VPN, "VPN service '%s' disappeared, cancelling connections",
+		             nm_vpn_service_get_name (service));
+		nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
+	}
+}
+
+/******************************************************************************/
+
+static void
+nm_vpn_service_init (NMVPNService *service)
+{
+	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+
+	priv->dbus_mgr = nm_dbus_manager_get ();
+	
+	priv->name_owner_id = g_signal_connect (priv->dbus_mgr, "name-owner-changed",
+									G_CALLBACK (nm_vpn_service_name_owner_changed),
+									service);
+}
+
+static gboolean
+ensure_killed (gpointer data)
+{
+	int pid = GPOINTER_TO_INT (data);
+
+	if (kill (pid, 0) == 0)
+		kill (pid, SIGKILL);
+
+	/* ensure the child is reaped */
+	nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", pid);
+	waitpid (pid, NULL, 0);
+	nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", pid);
+
+	return FALSE;
+}
+
+static void
+finalize (GObject *object)
+{
+	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object);
+
+	if (priv->service_start_timeout)
+		g_source_remove (priv->service_start_timeout);
+
+	nm_vpn_service_connections_stop (NM_VPN_SERVICE (object),
+	                                 FALSE,
+	                                 NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
+
+	g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id);
+
+	if (priv->service_child_watch)
+		g_source_remove (priv->service_child_watch);
+
+	if (priv->pid) {
+		if (kill (priv->pid, SIGTERM) == 0)
+			g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
+		else {
+			kill (priv->pid, SIGKILL);
+
+			/* ensure the child is reaped */
+			nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid);
+			waitpid (priv->pid, NULL, 0);
+			nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid);
+		}
+
+		priv->pid = 0;
+	}
+
+	g_object_unref (priv->dbus_mgr);
+
+	g_free (priv->name);
+	g_free (priv->dbus_service);
+	g_free (priv->program);
+
+	G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object);
+}
+
+static void
+nm_vpn_service_class_init (NMVPNServiceClass *service_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (service_class);
+
+	g_type_class_add_private (service_class, sizeof (NMVPNServicePrivate));
+
+	/* virtual methods */
+	object_class->finalize = finalize;
+}
diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h
new file mode 100644
index 00000000..9c5e0eef
--- /dev/null
+++ b/src/vpn-manager/nm-vpn-service.h
@@ -0,0 +1,60 @@
+/* -*- 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 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_VPN_SERVICE_H
+#define NM_VPN_SERVICE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "nm-device.h"
+#include "nm-vpn-connection.h"
+#include "nm-activation-request.h"
+
+#define NM_TYPE_VPN_SERVICE            (nm_vpn_service_get_type ())
+#define NM_VPN_SERVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_SERVICE, NMVPNService))
+#define NM_VPN_SERVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_SERVICE, NMVPNServiceClass))
+#define NM_IS_VPN_SERVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_SERVICE))
+#define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_SERVICE))
+#define NM_VPN_SERVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE, NMVPNServiceClass))
+
+typedef struct {
+	GObject parent;
+} NMVPNService;
+
+typedef struct {
+	GObjectClass parent;
+} NMVPNServiceClass;
+
+GType nm_vpn_service_get_type (void);
+
+NMVPNService * nm_vpn_service_new (const char *service_name);
+
+const char * nm_vpn_service_get_name (NMVPNService *service);
+
+NMVPNConnection * nm_vpn_service_activate (NMVPNService *service,
+                                           NMConnection *connection,
+                                           NMActRequest *act_request,
+                                           NMDevice *device,
+                                           GError **error);
+
+GSList * nm_vpn_service_get_active_connections (NMVPNService *service);
+
+#endif  /* NM_VPN_VPN_SERVICE_H */
diff --git a/src/wpa.c b/src/wpa.c
new file mode 100644
index 00000000..5f4fd024
--- /dev/null
+++ b/src/wpa.c
@@ -0,0 +1,455 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * WPA Supplicant - WPA state machine and EAPOL-Key processing
+ * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this file may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "wireless-helper.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+
+#include "wpa.h"
+#include "nm-logging.h"
+
+typedef guint16 u16;
+typedef guint8 u8;
+
+#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0]))
+
+#define BIT(n) (1 << (n))
+#define WPA_CAPABILITY_PREAUTH BIT(0)
+
+#define WPA_REPLAY_COUNTER_LEN 8
+#define WPA_NONCE_LEN 32
+
+#define PMKID_LEN 16
+
+static const int WPA_SELECTOR_LEN = 4;
+static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 };
+static const u16 WPA_VERSION = 1;
+static const u8 WPA_AUTH_KEY_MGMT_NONE[] = { 0x00, 0x50, 0xf2, 0 };
+static const u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 };
+static const u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 };
+static const u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 };
+static const u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 };
+static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 };
+static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 };
+static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 };
+static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 };
+
+/* WPA IE version 1
+ * 00-50-f2:1 (OUI:OUI type)
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: TKIP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: TKIP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ *    (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ *    (default: unspec 802.1X)
+ * WPA Capabilities (2 octets, little endian) (default: 0)
+ */
+
+struct wpa_ie_hdr {
+	u8 elem_id;
+	u8 len;
+	u8 oui[3];
+	u8 oui_type;
+	u8 version[2];
+} __attribute__ ((packed));
+
+
+static const int RSN_SELECTOR_LEN = 4;
+static const u16 RSN_VERSION = 1;
+static const u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 };
+static const u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 };
+static const u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 };
+static const u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 };
+static const u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 };
+static const u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 };
+static const u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 };
+static const u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 };
+
+/* EAPOL-Key Key Data Encapsulation
+ * GroupKey and STAKey require encryption, otherwise, encryption is optional.
+ */
+static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 };
+static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 };
+static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 };
+static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 };
+
+/* RSN IE version 1
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: CCMP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: CCMP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ *    (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ *    (default: unspec 802.1X)
+ * RSN Capabilities (2 octets, little endian) (default: 0)
+ * PMKID Count (2 octets) (default: 0)
+ * PMKID List (16 * n octets)
+ */
+
+struct rsn_ie_hdr {
+	u8 elem_id; /* WLAN_EID_RSN */
+	u8 len;
+	u8 version[2];
+} __attribute__ ((packed));
+
+
+#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
+#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
+#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */
+/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
+#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5))
+#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
+#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
+#define WPA_KEY_INFO_TXRX BIT(6) /* group */
+#define WPA_KEY_INFO_ACK BIT(7)
+#define WPA_KEY_INFO_MIC BIT(8)
+#define WPA_KEY_INFO_SECURE BIT(9)
+#define WPA_KEY_INFO_ERROR BIT(10)
+#define WPA_KEY_INFO_REQUEST BIT(11)
+#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
+
+
+
+static int wpa_selector_to_bitfield(const u8 *s)
+{
+	if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_NONE;
+	if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_WEP40;
+	if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_TKIP;
+	if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_CCMP;
+	if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_WEP104;
+	return 0;
+}
+
+
+static int wpa_key_mgmt_to_bitfield(const u8 *s)
+{
+	if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0)
+		return IW_AUTH_KEY_MGMT_802_1X;
+	if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) ==
+	    0)
+		return IW_AUTH_KEY_MGMT_PSK;
+	if (memcmp(s, WPA_AUTH_KEY_MGMT_NONE, WPA_SELECTOR_LEN) == 0)
+		return 0;
+	return 0;
+}
+
+
+static int rsn_selector_to_bitfield(const u8 *s)
+{
+	if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_NONE;
+	if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_WEP40;
+	if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_TKIP;
+	if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_CCMP;
+	if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0)
+		return IW_AUTH_CIPHER_WEP104;
+	return 0;
+}
+
+
+static int rsn_key_mgmt_to_bitfield(const u8 *s)
+{
+	if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0)
+		return IW_AUTH_KEY_MGMT_802_1X;
+	if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) ==
+	    0)
+		return IW_AUTH_KEY_MGMT_PSK;
+	return 0;
+}
+
+
+static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+				struct wpa_ie_data *data)
+{
+	const struct wpa_ie_hdr *hdr;
+	const u8 *pos;
+	int left;
+	int i, count;
+
+	data->proto = IW_AUTH_WPA_VERSION_WPA;
+	data->pairwise_cipher = IW_AUTH_CIPHER_TKIP;
+	data->group_cipher = IW_AUTH_CIPHER_TKIP;
+	data->key_mgmt = IW_AUTH_KEY_MGMT_802_1X;
+	data->capabilities = 0;
+	data->pmkid = NULL;
+	data->num_pmkid = 0;
+
+	if (wpa_ie_len == 0) {
+		/* No WPA IE - fail silently */
+		return -1;
+	}
+
+	if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
+		nm_log_dbg (LOGD_WIFI, "IE len too short %lu",
+			    (unsigned long) wpa_ie_len);
+		return -1;
+	}
+
+	hdr = (const struct wpa_ie_hdr *) wpa_ie;
+
+	if (hdr->elem_id != WPA_GENERIC_INFO_ELEM ||
+	    hdr->len != wpa_ie_len - 2 ||
+	    memcmp(hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 ||
+	    WPA_GET_LE16(hdr->version) != WPA_VERSION) {
+		nm_log_dbg (LOGD_WIFI, "malformed IE or unknown version");
+		return -1;
+	}
+
+	pos = (const u8 *) (hdr + 1);
+	left = wpa_ie_len - sizeof(*hdr);
+
+	if (left >= WPA_SELECTOR_LEN) {
+		data->group_cipher = wpa_selector_to_bitfield(pos);
+		pos += WPA_SELECTOR_LEN;
+		left -= WPA_SELECTOR_LEN;
+	} else if (left > 0) {
+		nm_log_dbg (LOGD_WIFI, "IE length mismatch, %u too much", left);
+		return -1;
+	}
+
+	if (left >= 2) {
+		data->pairwise_cipher = 0;
+		count = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+			nm_log_dbg (LOGD_WIFI, "IE count botch (pairwise), "
+			            "count %u left %u", count, left);
+			return -1;
+		}
+		for (i = 0; i < count; i++) {
+			data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+			pos += WPA_SELECTOR_LEN;
+			left -= WPA_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		nm_log_dbg (LOGD_WIFI, "IE too short (for key mgmt)");
+		return -1;
+	}
+
+	if (left >= 2) {
+		data->key_mgmt = 0;
+		count = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+			nm_log_dbg (LOGD_WIFI, "IE count botch (key mgmt), "
+				   "count %u left %u", count, left);
+			return -1;
+		}
+		for (i = 0; i < count; i++) {
+			data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+			pos += WPA_SELECTOR_LEN;
+			left -= WPA_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		nm_log_dbg (LOGD_WIFI, "IE too short (for capabilities)");
+		return -1;
+	}
+
+	if (left >= 2) {
+		if (WPA_GET_LE16 (pos) & WPA_CAPABILITY_PREAUTH)
+			data->capabilities |= IW_PMKID_CAND_PREAUTH;
+		pos += 2;
+		left -= 2;
+	}
+
+	if (left > 0) {
+		nm_log_dbg (LOGD_WIFI, "IE has %u trailing bytes", left);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
+				struct wpa_ie_data *data)
+{
+	const struct rsn_ie_hdr *hdr;
+	const u8 *pos;
+	int left;
+	int i, count;
+
+	data->proto = IW_AUTH_WPA_VERSION_WPA2;
+	data->pairwise_cipher = IW_AUTH_CIPHER_CCMP;
+	data->group_cipher = IW_AUTH_CIPHER_CCMP;
+	data->key_mgmt = IW_AUTH_KEY_MGMT_802_1X;
+	data->capabilities = 0;
+	data->pmkid = NULL;
+	data->num_pmkid = 0;
+
+	if (rsn_ie_len == 0) {
+		/* No RSN IE - fail silently */
+		return -1;
+	}
+
+	if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) {
+		nm_log_dbg (LOGD_WIFI, "IE len too short %lu",
+			    (unsigned long) rsn_ie_len);
+		return -1;
+	}
+
+	hdr = (const struct rsn_ie_hdr *) rsn_ie;
+
+	if (hdr->elem_id != WPA_RSN_INFO_ELEM ||
+	    hdr->len != rsn_ie_len - 2 ||
+	    WPA_GET_LE16(hdr->version) != RSN_VERSION) {
+		nm_log_dbg (LOGD_WIFI, "malformed IE or unknown version");
+		return -1;
+	}
+
+	pos = (const u8 *) (hdr + 1);
+	left = rsn_ie_len - sizeof(*hdr);
+
+	if (left >= RSN_SELECTOR_LEN) {
+		data->group_cipher = rsn_selector_to_bitfield(pos);
+		pos += RSN_SELECTOR_LEN;
+		left -= RSN_SELECTOR_LEN;
+	} else if (left > 0) {
+		nm_log_dbg (LOGD_WIFI, "IE length mismatch, %u too much", left);
+		return -1;
+	}
+
+	if (left >= 2) {
+		data->pairwise_cipher = 0;
+		count = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+			nm_log_dbg (LOGD_WIFI, "IE count botch (pairwise), "
+				    "count %u left %u", count, left);
+			return -1;
+		}
+		for (i = 0; i < count; i++) {
+			data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
+			pos += RSN_SELECTOR_LEN;
+			left -= RSN_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		nm_log_dbg (LOGD_WIFI, "IE too short (for key mgmt)");
+		return -1;
+	}
+
+	if (left >= 2) {
+		data->key_mgmt = 0;
+		count = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+			nm_log_dbg (LOGD_WIFI, "IE count botch (key mgmt), "
+				   "count %u left %u", count, left);
+			return -1;
+		}
+		for (i = 0; i < count; i++) {
+			data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
+			pos += RSN_SELECTOR_LEN;
+			left -= RSN_SELECTOR_LEN;
+		}
+	} else if (left == 1) {
+		nm_log_dbg (LOGD_WIFI, "IE too short (for capabilities)");
+		return -1;
+	}
+
+	if (left >= 2) {
+		if (WPA_GET_LE16 (pos) & WPA_CAPABILITY_PREAUTH)
+			data->capabilities |= IW_PMKID_CAND_PREAUTH;
+		pos += 2;
+		left -= 2;
+	}
+
+	if (left >= 2) {
+		data->num_pmkid = WPA_GET_LE16(pos);
+		pos += 2;
+		left -= 2;
+		if (left < data->num_pmkid * PMKID_LEN) {
+			nm_log_dbg (LOGD_WIFI, "PMKID underflow "
+				    "(num_pmkid=%d left=%d)",
+				    data->num_pmkid, left);
+			data->num_pmkid = 0;
+		} else {
+			data->pmkid = pos;
+			pos += data->num_pmkid * PMKID_LEN;
+			left -= data->num_pmkid * PMKID_LEN;
+		}
+	}
+
+	if (left > 0) {
+		nm_log_dbg (LOGD_WIFI, "IE has %u trailing bytes - ignored", left);
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_parse_wpa_ie - Parse WPA/RSN IE
+ * @wpa_ie: Pointer to WPA or RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @data: Pointer to data area for parsing results
+ * Returns: parsed results on success, NULL on failure
+ *
+ * Parse the contents of WPA or RSN IE and write the parsed data into data.
+ */
+wpa_ie_data * wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len)
+{
+	wpa_ie_data *data = NULL;
+	int err = -1;
+
+	if (!wpa_ie || wpa_ie_len <= 0)
+		return NULL;
+
+	data = g_slice_new0 (wpa_ie_data);
+
+	if (wpa_ie_len >= 1 && wpa_ie[0] == WPA_RSN_INFO_ELEM)
+		err = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+	else
+		err = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
+
+	if (err != 0) {
+		g_slice_free (wpa_ie_data, data);
+		data = NULL;
+	}
+
+	if (data) {
+		nm_log_dbg (LOGD_WIFI, "WPA IE: -------------------");
+		nm_log_dbg (LOGD_WIFI, "   proto        0x%X", data->proto);
+		nm_log_dbg (LOGD_WIFI, "   pw cipher    0x%X", data->pairwise_cipher);
+		nm_log_dbg (LOGD_WIFI, "   gr cipher    0x%X", data->group_cipher);
+		nm_log_dbg (LOGD_WIFI, "   key mgmt     0x%X", data->key_mgmt);
+		nm_log_dbg (LOGD_WIFI, "   capabilities 0x%X", data->capabilities);
+		nm_log_dbg (LOGD_WIFI, "   # pmkid      0x%X", data->num_pmkid);
+		nm_log_dbg (LOGD_WIFI, "");
+	}
+
+	return data;
+}
+
diff --git a/src/wpa.h b/src/wpa.h
new file mode 100644
index 00000000..18d1dbee
--- /dev/null
+++ b/src/wpa.h
@@ -0,0 +1,38 @@
+/*
+ * wpa_supplicant - WPA definitions
+ * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this file may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_H
+#define WPA_H
+
+#include <stdint.h>
+
+#define WPA_GENERIC_INFO_ELEM 0xdd
+#define WPA_RSN_INFO_ELEM 0x30
+
+#define WPA_MAX_IE_LEN 40
+
+typedef struct wpa_ie_data {
+	int proto;
+	int pairwise_cipher;
+	int group_cipher;
+	int key_mgmt;
+	int capabilities;
+	int num_pmkid;
+	const uint8_t *pmkid;
+} wpa_ie_data;
+
+
+wpa_ie_data * wpa_parse_wpa_ie (const uint8_t *wpa_ie, size_t wpa_ie_len);
+
+#endif /* WPA_H */