diff --git a/SOURCES/1004-preserve-routes-on-link-down-rh1676551.patch b/SOURCES/1004-preserve-routes-on-link-down-rh1676551.patch
new file mode 100644
index 0000000..e83937c
--- /dev/null
+++ b/SOURCES/1004-preserve-routes-on-link-down-rh1676551.patch
@@ -0,0 +1,769 @@
+From 51dd0ac4b8b7b13e3d26c2ab7d35e9451bbcd4c5 Mon Sep 17 00:00:00 2001
+From: Beniamino Galvani <bgalvani@redhat.com>
+Date: Fri, 21 Sep 2018 18:30:49 +0200
+Subject: [PATCH 1/5] core: improve nm_ip_config_dump()
+
+Previously we had nm_ip{4,6}_config_dump() for debugging purposes, but
+they were inconveniently printing to stdout and so the output was not
+ordered in the journal.
+
+Implement a unified nm_ip_config_dump() that logs through the usual
+logging mechanism.
+
+(cherry picked from commit 3b49d1075daa084edd7541a0915a97b3afd643e9)
+(cherry picked from commit 0b1ffdbdb599ecd53f4b0cdead4f64296ac3ad49)
+(cherry picked from commit 33aae9b8c4953c0bb2910cf792eece792ba4ba53)
+---
+ src/nm-ip4-config.c | 119 +++++++++++++++++++++++++-------------------
+ src/nm-ip4-config.h |   6 ++-
+ src/nm-ip6-config.c |  47 -----------------
+ src/nm-ip6-config.h |   1 -
+ 4 files changed, 74 insertions(+), 99 deletions(-)
+
+diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
+index 6ae81b573..ed6b712ea 100644
+--- a/src/nm-ip4-config.c
++++ b/src/nm-ip4-config.c
+@@ -1918,71 +1918,90 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
+ }
+ 
+ void
+-nm_ip4_config_dump (const NMIP4Config *self, const char *detail)
++nm_ip_config_dump (const NMIPConfig *self,
++                   const char *detail,
++                   NMLogLevel level,
++                   NMLogDomain domain)
+ {
+-	guint32 tmp;
+-	guint i;
+-	const char *str;
+ 	NMDedupMultiIter ipconf_iter;
+-	const NMPlatformIP4Address *address;
+-	const NMPlatformIP4Route *route;
+-
+-	g_message ("--------- NMIP4Config %p (%s)", self, detail);
++	const NMPlatformIP4Address *addr4;
++	const NMPlatformIP6Address *addr6;
++	const NMPlatformIP4Route *route4;
++	const NMPlatformIP6Route *route6;
++	const NMIP4Config *ip4;
++	const NMIP6Config *ip6;
++	int addr_family = AF_UNSPEC;
++	char addr_family_char = '?';
++	const char *path;
++	gconstpointer ptr;
++	guint i;
+ 
+-	if (self == NULL) {
+-		g_message (" (null)");
+-		return;
++	if (self) {
++		addr_family = nm_ip_config_get_addr_family (self);
++		addr_family_char = nm_utils_addr_family_to_char (addr_family);
+ 	}
+ 
+-	str = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
+-	if (str)
+-		g_message ("   path: %s", str);
+-
+-	/* addresses */
+-	nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, self, &address)
+-		g_message ("      a: %s", nm_platform_ip4_address_to_string (address, NULL, 0));
++	nm_log (level, domain, NULL, NULL,
++	        "---- NMIP%cConfig %p (%s)",
++	        addr_family_char,
++	        self,
++	        detail);
+ 
+-	/* nameservers */
+-	for (i = 0; i < nm_ip4_config_get_num_nameservers (self); i++) {
+-		tmp = nm_ip4_config_get_nameserver (self, i);
+-		g_message ("     ns: %s", nm_utils_inet4_ntop (tmp, NULL));
+-	}
+-
+-	/* routes */
+-	nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route)
+-		g_message ("     rt: %s", nm_platform_ip4_route_to_string (route, NULL, 0));
++	if (!self)
++		return;
+ 
+-	/* domains */
+-	for (i = 0; i < nm_ip4_config_get_num_domains (self); i++)
+-		g_message (" domain: %s", nm_ip4_config_get_domain (self, i));
++	path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
++	if (path)
++		nm_log (level, domain, NULL, NULL, " path     : %s", path);
+ 
+-	/* dns searches */
+-	for (i = 0; i < nm_ip4_config_get_num_searches (self); i++)
+-		g_message (" search: %s", nm_ip4_config_get_search (self, i));
++	if (addr_family == AF_INET) {
++		ip4 = NM_IP4_CONFIG (self);
++		nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &addr4) {
++			nm_log (level, domain, NULL, NULL, " address  : %s",
++			        nm_platform_ip4_address_to_string (addr4, NULL, 0));
++		}
++		nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route4) {
++			nm_log (level, domain, NULL, NULL, " route    : %s",
++			        nm_platform_ip4_route_to_string (route4, NULL, 0));
++		}
++	} else {
++		ip6 = NM_IP6_CONFIG (self);
++		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &addr6) {
++			nm_log (level, domain, NULL, NULL, " address  : %s",
++			        nm_platform_ip6_address_to_string (addr6, NULL, 0));
++		}
++		nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route6) {
++			nm_log (level, domain, NULL, NULL, " route    : %s",
++			        nm_platform_ip6_route_to_string (route6, NULL, 0));
++		}
++	}
+ 
+-	/* dns options */
+-	for (i = 0; i < nm_ip4_config_get_num_dns_options (self); i++)
+-		g_message (" dnsopt: %s", nm_ip4_config_get_dns_option (self, i));
++	for (i = 0; i < nm_ip_config_get_num_nameservers (self); i++) {
++		ptr = nm_ip_config_get_nameserver (self, i);
++		nm_log (level, domain, NULL, NULL, " dns      : %s",
++		        nm_utils_inet_ntop (addr_family, ptr, NULL));
++	}
+ 
+-	g_message (" dnspri: %d", nm_ip4_config_get_dns_priority (self));
++	for (i = 0; i < nm_ip_config_get_num_domains (self); i++)
++		nm_log (level, domain, NULL, NULL, " domain   : %s", nm_ip_config_get_domain (self, i));
+ 
+-	g_message ("    mtu: %"G_GUINT32_FORMAT" (source: %d)", nm_ip4_config_get_mtu (self), (int) nm_ip4_config_get_mtu_source (self));
++	for (i = 0; i < nm_ip_config_get_num_searches (self); i++)
++		nm_log (level, domain, NULL, NULL, " search   : %s", nm_ip_config_get_search (self, i));
+ 
+-	/* NIS */
+-	for (i = 0; i < nm_ip4_config_get_num_nis_servers (self); i++) {
+-		tmp = nm_ip4_config_get_nis_server (self, i);
+-		g_message ("    nis: %s", nm_utils_inet4_ntop (tmp, NULL));
+-	}
++	for (i = 0; i < nm_ip_config_get_num_dns_options (self); i++)
++		nm_log (level, domain, NULL, NULL, "dns-option: %s", nm_ip_config_get_dns_option (self, i));
+ 
+-	g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (self) ?: "(none)");
++	nm_log (level, domain, NULL, NULL, " dns-prio : %d", nm_ip_config_get_dns_priority (self));
+ 
+-	/* WINS */
+-	for (i = 0; i < nm_ip4_config_get_num_wins (self); i++) {
+-		tmp = nm_ip4_config_get_wins (self, i);
+-		g_message ("   wins: %s", nm_utils_inet4_ntop (tmp, NULL));
++	if (addr_family == AF_INET) {
++		ip4 = NM_IP4_CONFIG (self);
++		nm_log (level, domain, NULL, NULL,
++		        " mtu      : %"G_GUINT32_FORMAT" (source: %d)",
++		        nm_ip4_config_get_mtu (ip4),
++		        (int) nm_ip4_config_get_mtu_source (ip4));
++		nm_log (level, domain, NULL, NULL, " metered  : %d",
++		        (int) nm_ip4_config_get_metered (ip4));
+ 	}
+-
+-	g_message (" mtrd:   %d", (int) nm_ip4_config_get_metered (self));
+ }
+ 
+ /*****************************************************************************/
+diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
+index 78aca14e4..728b64ac6 100644
+--- a/src/nm-ip4-config.h
++++ b/src/nm-ip4-config.h
+@@ -191,7 +191,6 @@ NMIP4Config *nm_ip4_config_intersect_alloc (const NMIP4Config *a,
+                                             const NMIP4Config *b,
+                                             guint32 default_route_metric_penalty);
+ gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes);
+-void nm_ip4_config_dump (const NMIP4Config *self, const char *detail);
+ 
+ const NMPObject *nm_ip4_config_best_default_route_get (const NMIP4Config *self);
+ const NMPObject *_nm_ip4_config_best_default_route_find (const NMIP4Config *self);
+@@ -290,6 +289,11 @@ gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b);
+ 
+ gboolean _nm_ip_config_check_and_add_domain (GPtrArray *array, const char *domain);
+ 
++void nm_ip_config_dump (const NMIPConfig *self,
++                        const char *detail,
++                        NMLogLevel level,
++                        NMLogDomain domain);
++
+ /*****************************************************************************/
+ 
+ #include "nm-ip6-config.h"
+diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
+index 9807d3882..86b687e10 100644
+--- a/src/nm-ip6-config.c
++++ b/src/nm-ip6-config.c
+@@ -1452,53 +1452,6 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev
+ 	return has_relevant_changes || has_minor_changes;
+ }
+ 
+-void
+-nm_ip6_config_dump (const NMIP6Config *self, const char *detail)
+-{
+-	const struct in6_addr *tmp;
+-	guint32 i;
+-	const char *str;
+-	NMDedupMultiIter ipconf_iter;
+-	const NMPlatformIP6Address *address;
+-	const NMPlatformIP6Route *route;
+-
+-	g_return_if_fail (self != NULL);
+-
+-	g_message ("--------- NMIP6Config %p (%s)", self, detail);
+-
+-	str = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
+-	if (str)
+-		g_message ("   path: %s", str);
+-
+-	/* addresses */
+-	nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &address)
+-		g_message ("      a: %s", nm_platform_ip6_address_to_string (address, NULL, 0));
+-
+-	/* nameservers */
+-	for (i = 0; i < nm_ip6_config_get_num_nameservers (self); i++) {
+-		tmp = nm_ip6_config_get_nameserver (self, i);
+-		g_message ("     ns: %s", nm_utils_inet6_ntop (tmp, NULL));
+-	}
+-
+-	/* routes */
+-	nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &route)
+-		g_message ("     rt: %s", nm_platform_ip6_route_to_string (route, NULL, 0));
+-
+-	/* domains */
+-	for (i = 0; i < nm_ip6_config_get_num_domains (self); i++)
+-		g_message (" domain: %s", nm_ip6_config_get_domain (self, i));
+-
+-	/* dns searches */
+-	for (i = 0; i < nm_ip6_config_get_num_searches (self); i++)
+-		g_message (" search: %s", nm_ip6_config_get_search (self, i));
+-
+-	/* dns options */
+-	for (i = 0; i < nm_ip6_config_get_num_dns_options (self); i++)
+-		g_message (" dnsopt: %s", nm_ip6_config_get_dns_option (self, i));
+-
+-	g_message (" dnspri: %d", nm_ip6_config_get_dns_priority (self));
+-}
+-
+ /*****************************************************************************/
+ 
+ void
+diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
+index 9762ef48b..d4fed853c 100644
+--- a/src/nm-ip6-config.h
++++ b/src/nm-ip6-config.h
+@@ -135,7 +135,6 @@ NMIP6Config *nm_ip6_config_intersect_alloc (const NMIP6Config *a,
+                                             const NMIP6Config *b,
+                                             guint32 default_route_metric_penalty);
+ gboolean nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes);
+-void nm_ip6_config_dump (const NMIP6Config *self, const char *detail);
+ 
+ const NMPObject *nm_ip6_config_best_default_route_get (const NMIP6Config *self);
+ const NMPObject *_nm_ip6_config_best_default_route_find (const NMIP6Config *self);
+-- 
+2.20.1
+
+From 70e7474bff0ea9b57300804b9952c50bfaf3100c Mon Sep 17 00:00:00 2001
+From: Beniamino Galvani <bgalvani@redhat.com>
+Date: Tue, 25 Sep 2018 09:51:06 +0200
+Subject: [PATCH 2/5] device: fix updating device information after link change
+
+device_link_changed() can't use nm_device_update_from_platform_link()
+to update the device private fields because the latter overwrites
+priv->iface and priv->up, and so the checks below as:
+
+  if (info.name[0] && strcmp (priv->iface, info.name) != 0) {
+
+and:
+
+  was_up = priv->up;
+  priv->up = NM_FLAGS_HAS (info.n_ifi_flags, IFF_UP);
+  ...
+  if (priv->up && !was_up) {
+
+never succeed.
+
+Fixes: d7f7725ae8c965756902bd492bf2cf0834319548
+(cherry picked from commit 46ed756112f38f30f996bb7425514bc59b6f5360)
+(cherry picked from commit 81aaf0ff93c002dafd5198d677b802c1b161f757)
+(cherry picked from commit 4775b1dff20781340c05d09888b56b654aea0d1d)
+---
+ src/devices/nm-device.c | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index ac9e1da08..76474c2c3 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -3382,6 +3382,7 @@ device_link_changed (NMDevice *self)
+ 	gboolean ip_ifname_changed = FALSE;
+ 	nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL;
+ 	const NMPlatformLink *pllink;
++	const char *str;
+ 	int ifindex;
+ 	gboolean was_up;
+ 	gboolean update_unmanaged_specs = FALSE;
+@@ -3396,7 +3397,23 @@ device_link_changed (NMDevice *self)
+ 
+ 	pllink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (pllink));
+ 
+-	nm_device_update_from_platform_link (self, pllink);
++	str = nm_platform_link_get_udi (nm_device_get_platform (self), pllink->ifindex);
++	if (!nm_streq0 (str, priv->udi)) {
++		g_free (priv->udi);
++		priv->udi = g_strdup (str);
++		_notify (self, PROP_UDI);
++	}
++
++	if (!nm_streq0 (pllink->driver, priv->driver)) {
++		g_free (priv->driver);
++		priv->driver = g_strdup (pllink->driver);
++		_notify (self, PROP_DRIVER);
++	}
++
++	_set_mtu (self, pllink->mtu);
++
++	if (ifindex == nm_device_get_ip_ifindex (self))
++		_stats_update_counters_from_pllink (self, pllink);
+ 
+ 	had_hw_addr = (priv->hw_addr != NULL);
+ 	nm_device_update_hw_address (self);
+-- 
+2.20.1
+
+From 5a0ce5c98883f4812a1506ab4ac58758e19c5aab Mon Sep 17 00:00:00 2001
+From: Beniamino Galvani <bgalvani@redhat.com>
+Date: Fri, 21 Sep 2018 17:55:46 +0200
+Subject: [PATCH 3/5] ip-config: add @intersect_routes argument to intersect
+ functions
+
+In some cases we want to intersect two IP configurations without
+considering routes.
+
+(cherry picked from commit 8f07b3ac4f6878b3cb1e24c8f272e1bbfa26ba2f)
+(cherry picked from commit bd79e67c55a5c9d7736f9e0a43d557f26e54ab72)
+(cherry picked from commit da46fcaffdea2530f24fc697938ffe59e9e8d3c4)
+---
+ src/devices/nm-device.c | 23 ++++++++++++++---------
+ src/nm-ip4-config.c     | 13 +++++++++++--
+ src/nm-ip4-config.h     |  7 +++++++
+ src/nm-ip6-config.c     | 13 +++++++++++--
+ src/nm-ip6-config.h     |  2 ++
+ 5 files changed, 45 insertions(+), 13 deletions(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index 76474c2c3..2fad467a7 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -12059,7 +12059,9 @@ nm_device_get_firmware_missing (NMDevice *self)
+ }
+ 
+ static void
+-intersect_ext_config (NMDevice *self, AppliedConfig *config)
++intersect_ext_config (NMDevice *self,
++                      AppliedConfig *config,
++                      gboolean intersect_routes)
+ {
+ 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ 	NMIPConfig *ext;
+@@ -12076,10 +12078,11 @@ intersect_ext_config (NMDevice *self, AppliedConfig *config)
+ 	      : (NMIPConfig *) priv->ext_ip_config_6;
+ 
+ 	if (config->current)
+-		nm_ip_config_intersect (config->current, ext, penalty);
++		nm_ip_config_intersect (config->current, ext, intersect_routes, penalty);
+ 	else {
+ 		config->current = nm_ip_config_intersect_alloc (config->orig,
+ 		                                                ext,
++		                                                intersect_routes,
+ 		                                                penalty);
+ 	}
+ }
+@@ -12111,14 +12114,15 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
+ 				 * by the user. */
+ 				if (priv->con_ip_config_4) {
+ 					nm_ip4_config_intersect (priv->con_ip_config_4, priv->ext_ip_config_4,
++					                         TRUE,
+ 					                         default_route_metric_penalty_get (self, AF_INET));
+ 				}
+ 
+-				intersect_ext_config (self, &priv->dev_ip4_config);
+-				intersect_ext_config (self, &priv->wwan_ip_config_4);
++				intersect_ext_config (self, &priv->dev_ip4_config, TRUE);
++				intersect_ext_config (self, &priv->wwan_ip_config_4, TRUE);
+ 
+ 				for (iter = priv->vpn_configs_4; iter; iter = iter->next)
+-					nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, 0);
++					nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, TRUE, 0);
+ 			}
+ 
+ 			/* Remove parts from ext_ip_config_4 to only contain the information that
+@@ -12162,15 +12166,16 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
+ 				 * by the user. */
+ 				if (priv->con_ip_config_6) {
+ 					nm_ip6_config_intersect (priv->con_ip_config_6, priv->ext_ip_config_6,
++					                         TRUE,
+ 					                         default_route_metric_penalty_get (self, AF_INET6));
+ 				}
+ 
+-				intersect_ext_config (self, &priv->ac_ip6_config);
+-				intersect_ext_config (self, &priv->dhcp6.ip6_config);
+-				intersect_ext_config (self, &priv->wwan_ip_config_6);
++				intersect_ext_config (self, &priv->ac_ip6_config, TRUE);
++				intersect_ext_config (self, &priv->dhcp6.ip6_config, TRUE);
++				intersect_ext_config (self, &priv->wwan_ip_config_6, TRUE);
+ 
+ 				for (iter = priv->vpn_configs_6; iter; iter = iter->next)
+-					nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, 0);
++					nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, TRUE, 0);
+ 
+ 				if (   priv->ipv6ll_has
+ 				    && !nm_ip6_config_lookup_address (priv->ext_ip_config_6, &priv->ipv6ll_addr))
+diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
+index ed6b712ea..45f0861a8 100644
+--- a/src/nm-ip4-config.c
++++ b/src/nm-ip4-config.c
+@@ -1467,6 +1467,7 @@ nm_ip4_config_subtract (NMIP4Config *dst,
+ static gboolean
+ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
+                                  const NMIP4Config *src,
++                                 gboolean intersect_routes,
+                                  guint32 default_route_metric_penalty,
+                                  gboolean update_dst)
+ {
+@@ -1511,6 +1512,9 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
+ 	/* ignore nameservers */
+ 
+ 	/* routes */
++	if (!intersect_routes)
++		goto skip_routes;
++
+ 	changed = FALSE;
+ 	new_best_default_route = NULL;
+ 	nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, dst, &r) {
+@@ -1551,6 +1555,7 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
+ 		_notify (dst, PROP_GATEWAY);
+ 	}
+ 
++skip_routes:
+ 	if (changed) {
+ 		_notify_routes (dst);
+ 		result = TRUE;
+@@ -1580,9 +1585,10 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
+ void
+ nm_ip4_config_intersect (NMIP4Config *dst,
+                          const NMIP4Config *src,
++                         gboolean intersect_routes,
+                          guint32 default_route_metric_penalty)
+ {
+-	_nm_ip4_config_intersect_helper (dst, src, default_route_metric_penalty, TRUE);
++	_nm_ip4_config_intersect_helper (dst, src, intersect_routes, default_route_metric_penalty, TRUE);
+ }
+ 
+ /**
+@@ -1603,14 +1609,17 @@ nm_ip4_config_intersect (NMIP4Config *dst,
+ NMIP4Config *
+ nm_ip4_config_intersect_alloc (const NMIP4Config *a,
+                                const NMIP4Config *b,
++                               gboolean intersect_routes,
+                                guint32 default_route_metric_penalty)
+ {
+ 	NMIP4Config *a_copy;
+ 
+ 	if (_nm_ip4_config_intersect_helper ((NMIP4Config *) a, b,
++	                                     intersect_routes,
+ 	                                     default_route_metric_penalty, FALSE)) {
+ 		a_copy = nm_ip4_config_clone (a);
+-		_nm_ip4_config_intersect_helper (a_copy, b, default_route_metric_penalty, TRUE);
++		_nm_ip4_config_intersect_helper (a_copy, b, intersect_routes,
++		                                 default_route_metric_penalty, TRUE);
+ 		return a_copy;
+ 	} else
+ 		return NULL;
+diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
+index 728b64ac6..5806c81da 100644
+--- a/src/nm-ip4-config.h
++++ b/src/nm-ip4-config.h
+@@ -186,9 +186,11 @@ void nm_ip4_config_subtract (NMIP4Config *dst,
+                              guint32 default_route_metric_penalty);
+ void nm_ip4_config_intersect (NMIP4Config *dst,
+                               const NMIP4Config *src,
++                              gboolean intersect_routes,
+                               guint32 default_route_metric_penalty);
+ NMIP4Config *nm_ip4_config_intersect_alloc (const NMIP4Config *a,
+                                             const NMIP4Config *b,
++                                            gboolean intersect_routes,
+                                             guint32 default_route_metric_penalty);
+ gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes);
+ 
+@@ -534,11 +536,13 @@ nm_ip_config_best_default_route_get (const NMIPConfig *self)
+ static inline void
+ nm_ip_config_intersect (NMIPConfig *dst,
+                         const NMIPConfig *src,
++                        gboolean intersect_routes,
+                         guint32 default_route_metric_penalty)
+ {
+ 	_NM_IP_CONFIG_DISPATCH_SET_OP (, dst, src,
+ 	                               nm_ip4_config_intersect,
+ 	                               nm_ip6_config_intersect,
++	                               intersect_routes,
+ 	                               default_route_metric_penalty);
+ }
+ 
+@@ -580,18 +584,21 @@ nm_ip_config_replace (NMIPConfig *dst,
+ static inline NMIPConfig *
+ nm_ip_config_intersect_alloc (const NMIPConfig *a,
+                               const NMIPConfig *b,
++                              gboolean intersect_routes,
+                               guint32 default_route_metric_penalty)
+ {
+ 	if (NM_IS_IP4_CONFIG (a)) {
+ 		nm_assert (NM_IS_IP4_CONFIG (b));
+ 		return (NMIPConfig *) nm_ip4_config_intersect_alloc ((const NMIP4Config *) a,
+ 		                                                     (const NMIP4Config *) b,
++		                                                     intersect_routes,
+ 		                                                     default_route_metric_penalty);
+ 	} else {
+ 		nm_assert (NM_IS_IP6_CONFIG (a));
+ 		nm_assert (NM_IS_IP6_CONFIG (b));
+ 		return (NMIPConfig *) nm_ip6_config_intersect_alloc ((const NMIP6Config *) a,
+ 		                                                     (const NMIP6Config *) b,
++		                                                     intersect_routes,
+ 		                                                     default_route_metric_penalty);
+ 	}
+ }
+diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
+index 86b687e10..f2125659c 100644
+--- a/src/nm-ip6-config.c
++++ b/src/nm-ip6-config.c
+@@ -1053,6 +1053,7 @@ nm_ip6_config_subtract (NMIP6Config *dst,
+ static gboolean
+ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
+                                  const NMIP6Config *src,
++                                 gboolean intersect_routes,
+                                  guint32 default_route_metric_penalty,
+                                  gboolean update_dst)
+ {
+@@ -1097,6 +1098,9 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
+ 	/* ignore nameservers */
+ 
+ 	/* routes */
++	if (!intersect_routes)
++		goto skip_routes;
++
+ 	changed = FALSE;
+ 	new_best_default_route = NULL;
+ 	nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, dst, &r) {
+@@ -1141,6 +1145,7 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
+ 		result = TRUE;
+ 	}
+ 
++skip_routes:
+ 	/* ignore domains */
+ 	/* ignore dns searches */
+ 	/* ignore dns options */
+@@ -1163,9 +1168,10 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
+ void
+ nm_ip6_config_intersect (NMIP6Config *dst,
+                          const NMIP6Config *src,
++                         gboolean intersect_routes,
+                          guint32 default_route_metric_penalty)
+ {
+-	_nm_ip6_config_intersect_helper (dst, src, default_route_metric_penalty, TRUE);
++	_nm_ip6_config_intersect_helper (dst, src, intersect_routes, default_route_metric_penalty, TRUE);
+ }
+ 
+ /**
+@@ -1186,14 +1192,17 @@ nm_ip6_config_intersect (NMIP6Config *dst,
+ NMIP6Config *
+ nm_ip6_config_intersect_alloc (const NMIP6Config *a,
+                                const NMIP6Config *b,
++                               gboolean intersect_routes,
+                                guint32 default_route_metric_penalty)
+ {
+ 	NMIP6Config *a_copy;
+ 
+ 	if (_nm_ip6_config_intersect_helper ((NMIP6Config *) a, b,
++	                                     intersect_routes,
+ 	                                     default_route_metric_penalty, FALSE)) {
+ 		a_copy = nm_ip6_config_clone (a);
+-		_nm_ip6_config_intersect_helper (a_copy, b, default_route_metric_penalty, TRUE);
++		_nm_ip6_config_intersect_helper (a_copy, b, intersect_routes,
++		                                 default_route_metric_penalty, TRUE);
+ 		return a_copy;
+ 	} else
+ 		return NULL;
+diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
+index d4fed853c..4f1778060 100644
+--- a/src/nm-ip6-config.h
++++ b/src/nm-ip6-config.h
+@@ -130,9 +130,11 @@ void nm_ip6_config_subtract (NMIP6Config *dst,
+                              guint32 default_route_metric_penalty);
+ void nm_ip6_config_intersect (NMIP6Config *dst,
+                               const NMIP6Config *src,
++                              gboolean intersect_routes,
+                               guint32 default_route_metric_penalty);
+ NMIP6Config *nm_ip6_config_intersect_alloc (const NMIP6Config *a,
+                                             const NMIP6Config *b,
++                                            gboolean intersect_routes,
+                                             guint32 default_route_metric_penalty);
+ gboolean nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes);
+ 
+-- 
+2.20.1
+
+From 5078529926c6b634dc8af7dedb50d054c17c7558 Mon Sep 17 00:00:00 2001
+From: Beniamino Galvani <bgalvani@redhat.com>
+Date: Mon, 24 Sep 2018 16:35:25 +0200
+Subject: [PATCH 4/5] device: don't remove routes when the interface is down
+
+In update update_ext_ip_config() we remove from various internal
+configurations those addresses and routes that were removed externally
+by users.
+
+When the interface is brought down, the kernel automatically removes
+routes associated with it and so we should not consider them as
+"removed by users".
+
+Instead, keep them so that they can be restored when the interface
+comes up again.
+
+(cherry picked from commit f069c98cc95494891215ddf261661afb742744ba)
+(cherry picked from commit ce2d403530451a60357dcd1ce815977079f539f6)
+(cherry picked from commit 4fc6f5f3177903a23deda1384d9bb88fc5c933f4)
+---
+ src/devices/nm-device.c | 21 ++++++++++++---------
+ 1 file changed, 12 insertions(+), 9 deletions(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index 2fad467a7..e1bfe44e2 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -12093,6 +12093,7 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
+ 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ 	int ifindex;
+ 	GSList *iter;
++	gboolean is_up;
+ 
+ 	nm_assert_addr_family (addr_family);
+ 
+@@ -12100,6 +12101,8 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
+ 	if (!ifindex)
+ 		return FALSE;
+ 
++	is_up = nm_platform_link_is_up (nm_device_get_platform (self), ifindex);
++
+ 	if (addr_family == AF_INET) {
+ 
+ 		g_clear_object (&priv->ext_ip_config_4);
+@@ -12114,15 +12117,15 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
+ 				 * by the user. */
+ 				if (priv->con_ip_config_4) {
+ 					nm_ip4_config_intersect (priv->con_ip_config_4, priv->ext_ip_config_4,
+-					                         TRUE,
++					                         is_up,
+ 					                         default_route_metric_penalty_get (self, AF_INET));
+ 				}
+ 
+-				intersect_ext_config (self, &priv->dev_ip4_config, TRUE);
+-				intersect_ext_config (self, &priv->wwan_ip_config_4, TRUE);
++				intersect_ext_config (self, &priv->dev_ip4_config, is_up);
++				intersect_ext_config (self, &priv->wwan_ip_config_4, is_up);
+ 
+ 				for (iter = priv->vpn_configs_4; iter; iter = iter->next)
+-					nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, TRUE, 0);
++					nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, is_up, 0);
+ 			}
+ 
+ 			/* Remove parts from ext_ip_config_4 to only contain the information that
+@@ -12166,16 +12169,16 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
+ 				 * by the user. */
+ 				if (priv->con_ip_config_6) {
+ 					nm_ip6_config_intersect (priv->con_ip_config_6, priv->ext_ip_config_6,
+-					                         TRUE,
++					                         is_up,
+ 					                         default_route_metric_penalty_get (self, AF_INET6));
+ 				}
+ 
+-				intersect_ext_config (self, &priv->ac_ip6_config, TRUE);
+-				intersect_ext_config (self, &priv->dhcp6.ip6_config, TRUE);
+-				intersect_ext_config (self, &priv->wwan_ip_config_6, TRUE);
++				intersect_ext_config (self, &priv->ac_ip6_config, is_up);
++				intersect_ext_config (self, &priv->dhcp6.ip6_config, is_up);
++				intersect_ext_config (self, &priv->wwan_ip_config_6, is_up);
+ 
+ 				for (iter = priv->vpn_configs_6; iter; iter = iter->next)
+-					nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, TRUE, 0);
++					nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, is_up, 0);
+ 
+ 				if (   priv->ipv6ll_has
+ 				    && !nm_ip6_config_lookup_address (priv->ext_ip_config_6, &priv->ipv6ll_addr))
+-- 
+2.20.1
+
+From 79a55db94360c87add82e82f7b59818959d7e583 Mon Sep 17 00:00:00 2001
+From: Beniamino Galvani <bgalvani@redhat.com>
+Date: Thu, 20 Dec 2018 09:35:34 +0100
+Subject: [PATCH 5/5] device: ensure IP configuration is restored when link
+ goes up
+
+When the link is up and goes down link_changed_cb() schedules
+device_link_changed() to be run later. If the function is dispatched
+when the link is already up again, it does not detect that the link
+was down.
+
+Fix this by storing in the device state that we saw the link down so
+that device_link_changed() can properly restore the IP configuration.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1636715
+https://github.com/NetworkManager/NetworkManager/pull/264
+(cherry picked from commit 7bd193ef30fb2a1d4c328bb61412e35a5bccfced)
+(cherry picked from commit 710406e74635ec03776dc49aa2de6d2bc0f6ff5f)
+(cherry picked from commit c941b6fa101b2b6bceef86d2d5dac8342ba9065e)
+---
+ src/devices/nm-device.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index e1bfe44e2..a81541c87 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -382,6 +382,7 @@ typedef struct _NMDevicePrivate {
+ 
+ 	bool            ipv6ll_handle:1; /* TRUE if NM handles the device's IPv6LL address */
+ 	bool            ipv6ll_has:1;
++	bool            device_link_changed_down:1;
+ 
+ 	/* Generic DHCP stuff */
+ 	char *          dhcp_anycast_address;
+@@ -3387,8 +3388,10 @@ device_link_changed (NMDevice *self)
+ 	gboolean was_up;
+ 	gboolean update_unmanaged_specs = FALSE;
+ 	gboolean got_hw_addr = FALSE, had_hw_addr;
++	gboolean seen_down = priv->device_link_changed_down;
+ 
+ 	priv->device_link_changed_id = 0;
++	priv->device_link_changed_down = FALSE;
+ 
+ 	ifindex = nm_device_get_ifindex (self);
+ 	pllink = nm_platform_link_get (nm_device_get_platform (self), ifindex);
+@@ -3498,7 +3501,7 @@ device_link_changed (NMDevice *self)
+ 
+ 	device_recheck_slave_status (self, pllink);
+ 
+-	if (priv->up && !was_up) {
++	if (priv->up && (!was_up || seen_down)) {
+ 		/* the link was down and just came up. That happens for example, while changing MTU.
+ 		 * We must restore IP configuration. */
+ 		if (priv->ip4_state == IP_DONE) {
+@@ -3574,6 +3577,8 @@ link_changed_cb (NMPlatform *platform,
+ 	priv = NM_DEVICE_GET_PRIVATE (self);
+ 
+ 	if (ifindex == nm_device_get_ifindex (self)) {
++		if (!(info->n_ifi_flags & IFF_UP))
++			priv->device_link_changed_down = TRUE;
+ 		if (!priv->device_link_changed_id) {
+ 			priv->device_link_changed_id = g_idle_add ((GSourceFunc) device_link_changed, self);
+ 			_LOGD (LOGD_DEVICE, "queued link change for ifindex %d", ifindex);
+-- 
+2.20.1
+
diff --git a/SOURCES/1005-manager-ignore-ovs-system-master-when-assuming-rh1676551.patch b/SOURCES/1005-manager-ignore-ovs-system-master-when-assuming-rh1676551.patch
new file mode 100644
index 0000000..dcbce60
--- /dev/null
+++ b/SOURCES/1005-manager-ignore-ovs-system-master-when-assuming-rh1676551.patch
@@ -0,0 +1,36 @@
+From 14990584112a3740acb404f7085f1b5ed0cfb4f9 Mon Sep 17 00:00:00 2001
+From: Beniamino Galvani <bgalvani@redhat.com>
+Date: Tue, 26 Feb 2019 10:07:36 +0100
+Subject: [PATCH] manager: ignore ovs-system master when assuming
+ connections
+
+This change allows to assume after restart a device that has been
+enslaved externally to an ovs bridge.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1676551
+(cherry picked from commit 23b0f943b7408da77b51e83a16de604b52704a1d)
+---
+ src/nm-manager.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/src/nm-manager.c b/src/nm-manager.c
+index 289dcf838..225eb1f70 100644
+--- a/src/nm-manager.c
++++ b/src/nm-manager.c
+@@ -2405,7 +2405,12 @@ get_existing_connection (NMManager *self,
+ 	if (ifindex) {
+ 		int master_ifindex = nm_platform_link_get_master (priv->platform, ifindex);
+ 
+-		if (master_ifindex) {
++		/* Check that the master is activating before assuming a
++		 * slave connection. However, ignore ovs-system master as
++		 * we never manage it.
++		 */
++		if (   master_ifindex
++		    && nm_platform_link_get_type (priv->platform, master_ifindex) != NM_LINK_TYPE_OPENVSWITCH) {
+ 			master = nm_manager_get_device_by_ifindex (self, master_ifindex);
+ 			if (!master) {
+ 				_LOG2D (LOGD_DEVICE, device, "assume: don't assume because "
+-- 
+2.20.1
+
diff --git a/SOURCES/1006-device-announce-arp-after-slaves-rh1678796.patch b/SOURCES/1006-device-announce-arp-after-slaves-rh1678796.patch
new file mode 100644
index 0000000..213a499
--- /dev/null
+++ b/SOURCES/1006-device-announce-arp-after-slaves-rh1678796.patch
@@ -0,0 +1,107 @@
+From 17612d182004af4045b7e55654f231114e311f12 Mon Sep 17 00:00:00 2001
+From: Beniamino Galvani <bgalvani@redhat.com>
+Date: Thu, 21 Feb 2019 16:18:48 +0100
+Subject: [PATCH] device: do ARP announcements only after masters have a slave
+
+Delay ARP announcements for masters until the first interfaces gets
+enslaved. There is no point in doing it before as the ARP packets
+would be dropped in most cases; also, if the first slave is added when
+we already started announcing, the MAC of the master is going to
+change and so the remaining ARPs will have a wrong "sender mac
+address" field.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1678796
+
+https://github.com/NetworkManager/NetworkManager/pull/301
+(cherry picked from commit de1022285a6c09763d878cf60ef7e200343ae373)
+---
+ src/devices/nm-device.c | 38 +++++++++++++++++++++++++++++++++++---
+ 1 file changed, 35 insertions(+), 3 deletions(-)
+
+diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
+index a81541c87..a150442d3 100644
+--- a/src/devices/nm-device.c
++++ b/src/devices/nm-device.c
+@@ -2849,6 +2849,7 @@ find_slave_info (NMDevice *self, NMDevice *slave)
+ static gboolean
+ nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connection)
+ {
++	NMDevicePrivate *priv;
+ 	SlaveInfo *info;
+ 	gboolean success = FALSE;
+ 	gboolean configure;
+@@ -2857,6 +2858,7 @@ nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *c
+ 	g_return_val_if_fail (slave != NULL, FALSE);
+ 	g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL, FALSE);
+ 
++	priv = NM_DEVICE_GET_PRIVATE (self);
+ 	info = find_slave_info (self, slave);
+ 	if (!info)
+ 		return FALSE;
+@@ -2879,15 +2881,20 @@ nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *c
+ 	 */
+ 	nm_device_update_hw_address (self);
+ 
++	/* Send ARP announcements if did not yet and have addresses. */
++	if (   priv->ip4_state == IP_DONE
++	    && !priv->acd.announcing)
++		nm_device_arp_announce (self);
++
+ 	/* Restart IP configuration if we're waiting for slaves.  Do this
+ 	 * after updating the hardware address as IP config may need the
+ 	 * new address.
+ 	 */
+ 	if (success) {
+-		if (NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT)
++		if (priv->ip4_state == IP_WAIT)
+ 			nm_device_activate_stage3_ip4_start (self);
+ 
+-		if (NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_WAIT)
++		if (priv->ip6_state == IP_WAIT)
+ 			nm_device_activate_stage3_ip6_start (self);
+ 	}
+ 
+@@ -9795,6 +9802,7 @@ activate_stage5_ip4_config_result (NMDevice *self)
+ 	const char *method;
+ 	NMConnection *connection;
+ 	int ip_ifindex;
++	gboolean do_announce = FALSE;
+ 
+ 	req = nm_device_get_act_request (self);
+ 	g_assert (req);
+@@ -9840,7 +9848,31 @@ activate_stage5_ip4_config_result (NMDevice *self)
+ 		                           NULL, NULL, NULL);
+ 	}
+ 
+-	nm_device_arp_announce (self);
++	/* Send ARP announcements */
++
++	if (nm_device_is_master (self)) {
++		CList *iter;
++		SlaveInfo *info;
++
++		/* Skip announcement if there are no device enslaved, for two reasons:
++		 * 1) the master has a temporary MAC address until the first slave comes
++		 * 2) announcements are going to be dropped anyway without slaves
++		 */
++		do_announce = FALSE;
++
++		c_list_for_each (iter, &priv->slaves) {
++			info = c_list_entry (iter, SlaveInfo, lst_slave);
++			if (info->slave_is_enslaved) {
++				do_announce = TRUE;
++				break;
++			}
++		}
++	} else
++		do_announce = TRUE;
++
++	if (do_announce)
++		nm_device_arp_announce (self);
++
+ 	nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP4, FALSE);
+ 
+ 	/* Enter the IP_CHECK state if this is the first method to complete */
+-- 
+2.20.1
+
diff --git a/SPECS/NetworkManager.spec b/SPECS/NetworkManager.spec
index 083b184..33c0062 100644
--- a/SPECS/NetworkManager.spec
+++ b/SPECS/NetworkManager.spec
@@ -10,7 +10,7 @@
 %global epoch_version 1
 %global rpm_version 1.12.0
 %global real_version 1.12.0
-%global release_version 8
+%global release_version 10
 %global snapshot %{nil}
 %global git_sha %{nil}
 
@@ -130,6 +130,9 @@ Patch1000: 1000-cli-remove-assertion-in-nmc_device_state_to_color.patch
 Patch1001: 1001-translations-rh1569438.patch
 Patch1002: 1002-cli-fix-reading-vpn.secrets.-from-passwd-file.patch
 Patch1003: 1003-manager-accept-non-null-device-for-VPN-activations-rh1641174.patch
+Patch1004: 1004-preserve-routes-on-link-down-rh1676551.patch
+Patch1005: 1005-manager-ignore-ovs-system-master-when-assuming-rh1676551.patch
+Patch1006: 1006-device-announce-arp-after-slaves-rh1678796.patch
 
 # The pregenerated docs contain default values and paths that depend
 # on the configure options when creating the source tarball.
@@ -890,6 +893,13 @@ fi
 
 
 %changelog
+* Mon Mar  4 2019 Beniamino Galvani <bgalvani@redhat.com> - 1:1.12.0-10
+- manager: ignore ovs-system master when assuming connections (rh #1676551)
+- device: do ARP announcements only after masters have a slave (rh #1678796)
+
+* Tue Feb 19 2019 Beniamino Galvani <bgalvani@redhat.com> - 1:1.12.0-9
+- device: restore routes after a link down event (rh #1676551)
+
 * Fri Nov  2 2018 Thomas Haller <thaller@redhat.com> - 1:1.12.0-8
 - dhcp: fix out-of-bounds heap write for DHCPv6 with internal plugin (CVE-2018-15688)