diff --git a/SOURCES/0026-rh1139326-address-lifetime-dhcp-expiry.patch b/SOURCES/0026-rh1139326-address-lifetime-dhcp-expiry.patch
new file mode 100644
index 0000000..bcb0dce
--- /dev/null
+++ b/SOURCES/0026-rh1139326-address-lifetime-dhcp-expiry.patch
@@ -0,0 +1,246 @@
+diff -up NetworkManager-0.9.9.1/src/dhcp-manager/nm-dhcp-dhclient-utils.c.0026-rh1139326-address-lifetime-padding.orig NetworkManager-0.9.9.1/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+--- NetworkManager-0.9.9.1/src/dhcp-manager/nm-dhcp-dhclient-utils.c.0026-rh1139326-address-lifetime-padding.orig	2014-11-05 12:09:08.367289245 -0600
++++ NetworkManager-0.9.9.1/src/dhcp-manager/nm-dhcp-dhclient-utils.c	2014-11-05 12:11:32.453938494 -0600
+@@ -27,6 +27,7 @@
+ #include "nm-dhcp-dhclient-utils.h"
+ #include "nm-ip4-config.h"
+ #include "nm-utils.h"
++#include "NetworkManagerUtils.h"
+ 
+ #define CLIENTID_TAG            "send dhcp-client-identifier"
+ #define CLIENTID_FORMAT         CLIENTID_TAG " \"%s\"; # added by NetworkManager"
+@@ -530,6 +531,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (
+ 	GSList *parsed = NULL, *iter, *leases = NULL;
+ 	char **line, **split = NULL;
+ 	GHashTable *hash = NULL;
++	gint32 now_monotonic_ts;
+ 
+ 	g_return_val_if_fail (contents != NULL, NULL);
+ 
+@@ -570,6 +572,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (
+ 		g_date_time_ref (now);
+ 	else
+ 		now = g_date_time_new_now_utc ();
++	now_monotonic_ts = nm_utils_get_monotonic_timestamp_s ();
+ 
+ 	for (iter = parsed; iter; iter = g_slist_next (iter)) {
+ 		NMIP4Config *ip4;
+@@ -624,6 +627,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (
+ 		if (!address.plen)
+ 			address.plen = nm_utils_ip4_get_default_prefix (address.address);
+ 
++		address.timestamp = now_monotonic_ts;
+ 		address.lifetime = address.preferred = expiry;
+ 		address.source = NM_PLATFORM_SOURCE_DHCP;
+ 
+diff -up NetworkManager-0.9.9.1/src/platform/nm-platform.c.0026-rh1139326-address-lifetime-padding.orig NetworkManager-0.9.9.1/src/platform/nm-platform.c
+--- NetworkManager-0.9.9.1/src/platform/nm-platform.c.0026-rh1139326-address-lifetime-padding.orig	2014-03-26 10:07:56.000000000 -0500
++++ NetworkManager-0.9.9.1/src/platform/nm-platform.c	2014-11-05 12:12:50.787378771 -0600
+@@ -1370,14 +1370,71 @@ array_contains_ip6_address (const GArray
+ 	return FALSE;
+ }
+ 
+-/* Compute (a - b) in an overflow-safe manner. */
++/**
++ * Takes a pair @timestamp and @duration, and returns the remaining duration based
++ * on the new timestamp @now.
++ */
+ static guint32
+-subtract_guint32 (guint32 a, guint32 b)
++_rebase_relative_time_on_now (guint32 timestamp, guint32 duration, guint32 now, guint32 padding)
++{
++	gint64 t;
++
++	if (duration == NM_PLATFORM_LIFETIME_PERMANENT)
++		return NM_PLATFORM_LIFETIME_PERMANENT;
++
++	if (timestamp == 0) {
++		/* if the @timestamp is zero, assume it was just left unset and that the relative
++		 * @duration starts counting from @now. This is convenient to construct an address
++		 * and print it in nm_platform_ip4_address_to_string().
++		 *
++		 * In general it does not make sense to set the @duration without anchoring at
++		 * @timestamp because you don't know the absolute expiration time when looking
++		 * at the address at a later moment. */
++		timestamp = now;
++	}
++
++	/* For timestamp > now, just accept it and calculate the expected(?) result. */
++	t = (gint64) timestamp + (gint64) duration - (gint64) now;
++
++	/* Optional padding to avoid potential races. */
++	t += (gint64) padding;
++
++	return (guint32) CLAMP (t, 0, NM_PLATFORM_LIFETIME_PERMANENT - 1);
++}
++
++static gboolean
++_address_get_lifetime (guint32 timestamp,
++                       guint32 orig_lifetime,
++                       guint32 orig_preferred,
++                       guint32 now,
++                       guint32 padding,
++                       guint32 *out_lifetime,
++                       guint32 *out_preferred)
+ {
+-	if (a == G_MAXUINT32)
+-		return G_MAXUINT32;
++	guint32 lifetime, preferred;
++
++	if (orig_lifetime == 0) {
++		*out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
++		*out_preferred = NM_PLATFORM_LIFETIME_PERMANENT;
++	} else {
++		lifetime = _rebase_relative_time_on_now (timestamp, orig_lifetime, now, padding);
++		if (!lifetime)
++			return FALSE;
++		preferred = _rebase_relative_time_on_now (timestamp, orig_preferred, now, padding);
++
++		*out_lifetime = lifetime;
++		*out_preferred = MIN (preferred, lifetime);
+ 
+-	return a > b ? a - b : 0;
++		/* Assert that non-permanent addresses have a (positive) @timestamp. _rebase_relative_time_on_now()
++		 * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
++		 * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
++		 */
++		g_return_val_if_fail (   timestamp != 0
++		                      || (   orig_lifetime  == NM_PLATFORM_LIFETIME_PERMANENT
++		                          && orig_preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE);
++		g_return_val_if_fail (preferred <= lifetime, TRUE);
++	}
++	return TRUE;
+ }
+ 
+ /**
+@@ -1417,14 +1474,9 @@ nm_platform_ip4_address_sync (int ifinde
+ 		const NMPlatformIP4Address *known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i);
+ 		guint32 lifetime, preferred;
+ 
+-		if (known_address->lifetime) {
+-			/* Pad the timestamp by 5 seconds to avoid potential races. */
+-			guint32 shift = subtract_guint32 (now, known_address->timestamp + 5);
+-
+-			lifetime = subtract_guint32 (known_address->lifetime, shift);
+-			preferred = subtract_guint32 (known_address->lifetime, shift);
+-		} else
+-			lifetime = preferred = NM_PLATFORM_LIFETIME_PERMANENT;
++		/* add a padding of 5 seconds to avoid potential races. */
++		if (!_address_get_lifetime (known_address->timestamp, known_address->lifetime, known_address->preferred, now, 5, &lifetime, &preferred))
++			continue;
+ 
+ 		if (!nm_platform_ip4_address_add (ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label))
+ 			return FALSE;
+@@ -1474,14 +1526,9 @@ nm_platform_ip6_address_sync (int ifinde
+ 		const NMPlatformIP6Address *known_address = &g_array_index (known_addresses, NMPlatformIP6Address, i);
+ 		guint32 lifetime, preferred;
+ 
+-		if (known_address->lifetime) {
+-			/* Pad the timestamp by 5 seconds to avoid potential races. */
+-			guint32 shift = subtract_guint32 (now, known_address->timestamp + 5);
+-
+-			lifetime = subtract_guint32 (known_address->lifetime, shift);
+-			preferred = subtract_guint32 (known_address->lifetime, shift);
+-		} else
+-			lifetime = preferred = NM_PLATFORM_LIFETIME_PERMANENT;
++		/* add a padding of 5 seconds to avoid potential races. */
++		if (!_address_get_lifetime (known_address->timestamp, known_address->lifetime, known_address->preferred, now, 5, &lifetime, &preferred))
++			continue;
+ 
+ 		if (!nm_platform_ip6_address_add (ifindex, known_address->address,
+ 		                                  known_address->peer_address, known_address->plen,
+
+From 90a77565d4c2e92e5798c463ca1e3baa4de51662 Mon Sep 17 00:00:00 2001
+From: Dan Williams <dcbw@redhat.com>
+Date: Tue, 23 Sep 2014 15:44:55 -0500
+Subject: [PATCH] dhcp: treat lease expiry as failure (rh #1139326)
+
+Lease expiry means that the DHCP configuration is no longer valid, and
+that all attempts to renew/rebind the lease have failed.  The IP config
+needs to be removed.  NetworkManager also sets prefered/valid lifetimes
+on addresses, so the kernel will remove them when the lease expires
+anyway.  That causes removal of the default route, if the default route
+was through the device whose config has now expired.
+
+DHCP clients will typically move to the 'renew' or 'rebind' states when
+nearing lease expiry, then if no answer is received move to the 'expire'
+state.  Eventually they move to the 'fail' state when all attempts to
+contact the server have failed.
+
+Previously, since NM ignored the 'expire' DHCP state it would not clear
+out the DHCP IP4 config immediately when the lease expired, instead
+waiting for the DHCP client to move to the 'fail' state.  But if the
+DHCP server appeared between the 'expire' and 'fail' states, NM would
+not notice and the device's NMIP4Config would not change, and thus the
+Policy would not get the "ip4-config-changed" signal to re-add the
+default route that the kernel had previously removed due to the valid
+lifetime reaching zero when the lease expired.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1139326
+
+diff -up NetworkManager-0.9.9.1/src/devices/nm-device.c.foo NetworkManager-0.9.9.1/src/devices/nm-device.c
+--- NetworkManager-0.9.9.1/src/devices/nm-device.c.foo	2014-11-19 14:10:04.598128259 -0600
++++ NetworkManager-0.9.9.1/src/devices/nm-device.c	2014-11-19 14:14:11.661710806 -0600
+@@ -2749,6 +2749,10 @@ dhcp4_state_changed (NMDHCPClient *clien
+ 	case DHC_TIMEOUT: /* timed out contacting DHCP server */
+ 		dhcp4_fail (device, TRUE);
+ 		break;
++	case DHC_EXPIRE: /* lease expired */
++		if (priv->ip4_state != IP_DONE)
++			break;
++		/* fall through */
+ 	case DHC_END: /* dhclient exited normally */
+ 	case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
+ 	case DHC_ABEND: /* dhclient exited abnormally */
+@@ -3160,6 +3164,10 @@ dhcp6_state_changed (NMDHCPClient *clien
+ 	case DHC_TIMEOUT: /* timed out contacting DHCP server */
+ 		dhcp6_fail (device, TRUE);
+ 		break;
++	case DHC_EXPIRE6: /* lease expired */
++		if (priv->ip6_state != IP_DONE)
++			break;
++		/* fall through */
+ 	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
+
+From acee2eb9e4954eb33b85fa10a3522b8253271126 Mon Sep 17 00:00:00 2001
+From: Dan Williams <dcbw@redhat.com>
+Date: Tue, 23 Sep 2014 15:38:33 -0500
+Subject: [PATCH] dhcp: fail the device if DHCP fails after having succeeded
+ earlier (rh #1139326)
+
+If DHCP fails to renew or rebind a lease, fail the device since the
+IP config is no longer valid.  Commit e2b7c482 was actually wrong for
+dhcp[4|6]_fail(), since (ip_state == IP_FAIL) will never be true if
+DHCP has ever been started, as IP_FAIL is only set from
+nm_device_activate_ip[4|6]_config_timeout(), which obviously will not
+be called in DHCP code paths if DHCP has previously succeeded.
+
+diff -up NetworkManager-0.9.9.1/src/devices/nm-device.c.foo NetworkManager-0.9.9.1/src/devices/nm-device.c
+--- NetworkManager-0.9.9.1/src/devices/nm-device.c.foo	2014-11-20 18:19:56.260016368 -0600
++++ NetworkManager-0.9.9.1/src/devices/nm-device.c	2014-11-20 20:52:00.302177894 -0600
+@@ -2700,8 +2700,10 @@ dhcp4_fail (NMDevice *device, gboolean t
+ 
+ 	if (timeout || (priv->ip4_state == IP_CONF))
+ 		nm_device_activate_schedule_ip4_config_timeout (device);
+-	else if (priv->ip4_state == IP_FAIL)
++	else if (priv->ip4_state == IP_DONE)
+ 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
++	else
++		g_warn_if_reached ();
+ }
+ 
+ static void
+@@ -3120,8 +3122,10 @@ dhcp6_fail (NMDevice *device, gboolean t
+ 
+ 	if (timeout || (priv->ip6_state == IP_CONF))
+ 		nm_device_activate_schedule_ip6_config_timeout (device);
+-	else if (priv->ip6_state == IP_FAIL)
++	else if (priv->ip6_state == IP_DONE)
+ 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
++	else
++		g_warn_if_reached ();
+ }
+ 
+ static void
diff --git a/SPECS/NetworkManager.spec b/SPECS/NetworkManager.spec
index 8f651b9..5441714 100644
--- a/SPECS/NetworkManager.spec
+++ b/SPECS/NetworkManager.spec
@@ -28,7 +28,7 @@ Name: NetworkManager
 Summary: Network connection manager and user applications
 Epoch: 1
 Version: %{realversion}
-Release: 28%{snapshot}%{?git_sha}%{?dist}.2
+Release: 29%{snapshot}%{?git_sha}%{?dist}
 Group: System Environment/Base
 License: GPLv2+
 URL: http://www.gnome.org/projects/NetworkManager/
@@ -62,6 +62,7 @@ Patch22: 0022-rh1112020-crash-reading-bridge-sysctl.patch
 Patch23: 0023-rh1093231-mtu-fix.patch
 #Patch24: 0024-rh1083133-rh1098319-nm-ipv6ll.patch
 Patch25: 0025-rh1149996-ifcfg-rh-GATEWAY.patch
+Patch26: 0026-rh1139326-address-lifetime-dhcp-expiry.patch
 
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
@@ -236,6 +237,7 @@ by nm-connection-editor and nm-applet in a non-graphical environment.
 %patch23 -p1 -b .0023-rh1093231-mtu-fix.orig
 #%patch24 -p1 -b .0024-rh1083133-rh1098319-nm-ipv6ll.orig
 %patch25 -p1 -b .0025-rh1149996-ifcfg-rh-GATEWAY.orig
+%patch26 -p1 -b .0026-rh1139326-address-lifetime-padding.orig
 
 %build
 
@@ -457,6 +459,9 @@ fi
 %endif
 
 %changelog
+* Tue Dec  2 2014 Dan Williams <dcbw@redhat.com> - 1:0.9.9.1-29.git20140326
+- core: fix address lifetime padding and DHCP lease expiry (rh #1139326) (rh #1160160)
+
 * Wed Oct  8 2014 Jiří Klimeš <jklimes@redhat.com> - 1:0.9.9.1-28.git20140326.2
 - ifcfg-rh: write GATEWAY instead of GATEWAY0 to be ifup-compatible (rh #1149996)