summaryrefslogtreecommitdiff
path: root/extra/networkmanager/dont-fight-over-ipv6.patch
blob: f7945335f524cf1355d97fa169fb84a1cf84f23a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
From 77de91e5a8b1c1993ae65c54b37e0411e78e6fe6 Mon Sep 17 00:00:00 2001
From: Dan Winship <danw@gnome.org>
Date: Thu, 19 Apr 2012 18:27:12 +0000
Subject: core: don't fight with the kernel over the default IPv6 route

The kernel wants there to be a default route over every RA-ed IPv6
interface, and it gets confused and annoyed if we remove that default
route and replace it with our own (causing it to effectively drop all
further RAs on the floor, which is particularly bad if some of the
information in the earlier RA had an expiration time).

So, rather than replacing the kernel's default route(s), just add an
additional one of our own, with a lower (ie, higher priority) metric.

https://bugzilla.redhat.com/show_bug.cgi?id=785772
---
diff --git a/src/nm-system.c b/src/nm-system.c
index 91153ec..4cebb13 100644
--- a/src/nm-system.c
+++ b/src/nm-system.c
@@ -1023,7 +1023,7 @@ add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw)
 }
 
 static int
-replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
+add_default_ip6_route (int ifindex, const struct in6_addr *gw)
 {
 	struct rtnl_route *route = NULL;
 	struct nl_sock *nlh;
@@ -1037,22 +1037,36 @@ replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
 	route = nm_netlink_route_new (ifindex, AF_INET6, 0,
 	                              NMNL_PROP_SCOPE, RT_SCOPE_UNIVERSE,
 	                              NMNL_PROP_TABLE, RT_TABLE_MAIN,
+	                              NMNL_PROP_PRIO, 1,
 	                              NULL);
 	g_return_val_if_fail (route != NULL, -ENOMEM);
 
 	/* Add the new default route */
-	err = nm_netlink_route6_add (route, &in6addr_any, 0, gw, NLM_F_REPLACE);
-	if (err == -NLE_EXIST) {
-		/* FIXME: even though we use NLM_F_REPLACE the kernel won't replace
-		 * the route if it's the same.  Suppress the pointless error.
-		 */
+	err = nm_netlink_route6_add (route, &in6addr_any, 0, gw, NLM_F_CREATE);
+	if (err == -NLE_EXIST)
 		err = 0;
-	}
 
 	rtnl_route_put (route);
 	return err;
 }
 
+static struct rtnl_route *
+find_static_default_routes (struct rtnl_route *route,
+                            struct nl_addr *dst,
+                            const char *iface,
+                            gpointer user_data)
+{
+	GList **def_routes = user_data;
+
+	if (   nl_addr_get_prefixlen (dst) == 0
+	    && rtnl_route_get_protocol (route) == RTPROT_STATIC) {
+		rtnl_route_get (route);
+		*def_routes = g_list_prepend (*def_routes, route);
+	}
+
+	return NULL;
+}
+
 /*
  * nm_system_replace_default_ip6_route
  *
@@ -1062,12 +1076,35 @@ replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
 gboolean
 nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
 {
-	struct rtnl_route *gw_route = NULL;
+	GList *def_routes, *iter;
+	struct rtnl_route *route, *gw_route = NULL;
 	gboolean success = FALSE;
 	char *iface;
 	int err;
 
-	err = replace_default_ip6_route (ifindex, gw);
+	/* We can't just use NLM_F_REPLACE here like in the IPv4 case, because
+	 * the kernel doesn't like it if we replace the default routes it
+	 * creates. (See rh#785772.) So we delete any non-kernel default routes,
+	 * and then add a new default route of our own with a lower metric than
+	 * the kernel ones.
+	 */
+	def_routes = NULL;
+	nm_netlink_foreach_route (ifindex, AF_INET6, RT_SCOPE_UNIVERSE, TRUE,
+	                          find_static_default_routes, &def_routes);
+	for (iter = def_routes; iter; iter = iter->next) {
+		route = iter->data;
+		if (!nm_netlink_route_delete (route)) {
+			iface = nm_netlink_index_to_iface (ifindex);
+			nm_log_err (LOGD_DEVICE | LOGD_IP6,
+			            "(%s): failed to delete existing IPv6 default route",
+			            iface);
+			g_free (iface);
+		}
+		rtnl_route_put (route);
+	}
+	g_list_free (def_routes);
+
+	err = add_default_ip6_route (ifindex, gw);
 	if (err == 0)
 		return TRUE;
 
@@ -1091,7 +1128,7 @@ nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
 		goto out;
 
 	/* Try adding the original route again */
-	err = replace_default_ip6_route (ifindex, gw);
+	err = add_default_ip6_route (ifindex, gw);
 	if (err != 0) {
 		nm_netlink_route_delete (gw_route);
 		nm_log_err (LOGD_DEVICE | LOGD_IP6,
--
cgit v0.9.0.2-2-gbebe