summary refs log tree commit diff
path: root/src/nm-system.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-system.c')
-rw-r--r--src/nm-system.c192
1 files changed, 155 insertions, 37 deletions
diff --git a/src/nm-system.c b/src/nm-system.c
index 35aa36a1..7921fec6 100644
--- a/src/nm-system.c
+++ b/src/nm-system.c
@@ -44,7 +44,6 @@
 
 #include "nm-system.h"
 #include "nm-device.h"
-#include "nm-named-manager.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
 #include "nm-logging.h"
@@ -481,32 +480,34 @@ nm_system_apply_ip4_config (const char *iface,
 	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)
+int
+nm_system_set_ip6_route (int ifindex,
+                         const struct in6_addr *ip6_dest,
+                         guint32 ip6_prefix,
+                         const struct in6_addr *ip6_gateway,
+                         guint32 metric,
+                         int mss,
+                         int protocol,
+                         int table,
+                         struct rtnl_route **out_route)
 {
 	struct nl_handle *nlh;
 	struct rtnl_route *route;
 	struct nl_addr *dest_addr;
 	struct nl_addr *gw_addr = NULL;
-	int err, iface_idx;
+	int err = 0;
 
-	nlh = nm_netlink_get_default_handle ();
-	g_return_val_if_fail (nlh != NULL, NULL);
+	g_return_val_if_fail (ifindex >= 0, -1);
 
-	iface_idx = nm_netlink_iface_to_index (iface);
-	g_return_val_if_fail (iface_idx >= 0, NULL);
+	nlh = nm_netlink_get_default_handle ();
+	g_return_val_if_fail (nlh != NULL, -1);
 
-	route = create_route (iface_idx, mss);
-	g_return_val_if_fail (route != NULL, NULL);
+	route = create_route (ifindex, mss);
+	g_return_val_if_fail (route != NULL, -1);
 
 	/* 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);
+	g_return_val_if_fail (dest_addr != NULL, -1);
 	nl_addr_set_prefixlen (dest_addr, (int) ip6_prefix);
 
 	rtnl_route_set_dst (route, dest_addr);
@@ -521,7 +522,7 @@ nm_system_device_set_ip6_route (const char *iface,
 		} else {
 			nm_log_warn (LOGD_DEVICE | LOGD_IP6, "Invalid gateway");
 			rtnl_route_put (route);
-			return NULL;
+			return -1;
 		}
 	}
 
@@ -529,13 +530,19 @@ nm_system_device_set_ip6_route (const char *iface,
 	if (metric)
 		rtnl_route_set_prio (route, metric);
 
+	if (protocol)
+		rtnl_route_set_protocol (route, protocol);
+
+	if (table)
+		rtnl_route_set_table (route, table);
+
 	/* 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);
+		route2 = create_route (ifindex, mss);
 		if (route2) {
 			/* Add route to gateway over bridge */
 			rtnl_route_set_dst (route2, gw_addr);
@@ -553,15 +560,12 @@ nm_system_device_set_ip6_route (const char *iface,
 	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 ());
+	if (out_route)
+		*out_route = route;
+	else
 		rtnl_route_put (route);
-		route = NULL;
-	}
 
-	return route;
+	return err;
 }
 
 static gboolean
@@ -618,24 +622,33 @@ nm_system_apply_ip6_config (const char *iface,
 	}
 
 	if (flags & NM_IP6_COMPARE_FLAG_ROUTES) {
+		int ifindex = nm_netlink_iface_to_index (iface);
+
 		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;
+			int err;
 
 			/* 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)))
+			    && 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);
+			err = nm_system_set_ip6_route (ifindex,
+			                               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),
+			                               RTPROT_UNSPEC,
+			                               RT_TABLE_UNSPEC,
+			                               NULL);
+			if (err) {
+				nm_log_err (LOGD_DEVICE | LOGD_IP6,
+				            "(%s): failed to set IPv6 route: %s",
+				            iface, nl_geterror ());
+			}
 		}
 	}
 
@@ -772,6 +785,48 @@ nm_system_device_set_mtu (const char *iface, guint32 mtu)
 	return success;
 }
 
+gboolean
+nm_system_device_set_mac (const char *iface, const struct ether_addr *mac)
+{
+	struct rtnl_link *old;
+	struct rtnl_link *new;
+	gboolean success = FALSE;
+	struct nl_handle *nlh;
+	int iface_idx;
+	struct nl_addr *addr = NULL;
+
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (mac != NULL, 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) {
+		addr = nl_addr_build (AF_LLC, (void *) mac, ETH_ALEN);
+		if (!addr) {
+			char *mac_str;
+			mac_str = 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]);
+			nm_log_err (LOGD_DEVICE, "(%s): could not allocate memory for MAC address (%s)", iface, mac_str);
+			g_free (mac_str);
+			return FALSE;
+		}
+		rtnl_link_set_addr (new, addr);
+		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)
 {
@@ -1183,6 +1238,45 @@ foreach_route (void (*callback)(struct nl_object *, gpointer),
 	nl_cache_free (route_cache);
 }
 
+static void
+dump_route (struct rtnl_route *route)
+{
+	char buf6[INET6_ADDRSTRLEN];
+	char buf4[INET_ADDRSTRLEN];
+	struct nl_addr *nl;
+	struct in6_addr *addr6 = NULL;
+	struct in_addr *addr4 = NULL;
+	int prefixlen = 0;
+	const char *sf = "UNSPEC";
+	int family = rtnl_route_get_family (route);
+
+	memset (buf6, 0, sizeof (buf6));
+	memset (buf4, 0, sizeof (buf4));
+	nl = rtnl_route_get_dst (route);
+	if (nl) {
+		if (nl_addr_get_family (nl) == AF_INET) {
+			addr4 = nl_addr_get_binary_addr (nl);
+			if (addr4)
+				inet_ntop (AF_INET, addr4, &buf4[0], sizeof (buf4));
+		} else if (nl_addr_get_family (nl) == AF_INET6) {
+			addr6 = nl_addr_get_binary_addr (nl);
+			if (addr6)
+				inet_ntop (AF_INET6, addr6, &buf6[0], sizeof (buf6));
+		}
+		prefixlen = nl_addr_get_prefixlen (nl);
+	}
+
+	if (family == AF_INET)
+		sf = "INET";
+	else if (family == AF_INET6)
+		sf = "INET6";
+
+	nm_log_dbg (LOGD_IP4 | LOGD_IP6, "  route idx %d family %s (%d) addr %s/%d",
+	            rtnl_route_get_oif (route),
+	            sf, family,
+	            strlen (buf4) ? buf4 : (strlen (buf6) ? buf6 : "<unknown>"),
+	            prefixlen);
+}
 
 typedef struct {
 	const char *iface;
@@ -1196,6 +1290,10 @@ 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;
+	guint32 log_level = LOGD_IP4 | LOGD_IP6;
+
+	if (nm_logging_level_enabled (LOGL_DEBUG))
+		dump_route (route);
 
 	/* Delete all routes from this interface */
 	if (rtnl_route_get_oif (route) != data->iface_idx)
@@ -1217,22 +1315,32 @@ check_one_route (struct nl_object *object, void *user_data)
 			addr = nl_addr_get_binary_addr (nl);
 
 		if (addr) {
-			if (IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MC_LINKLOCAL (addr))
+			if (   IN6_IS_ADDR_LINKLOCAL (addr)
+			    || IN6_IS_ADDR_MC_LINKLOCAL (addr)
+			    || (IN6_IS_ADDR_MULTICAST (addr) && (nl_addr_get_prefixlen (nl) == 8)))
 				return;
 		}
 	}
 
+	if (data->family == AF_INET)
+		log_level = LOGD_IP4;
+	else if (data->family == AF_INET6)
+		log_level = LOGD_IP6;
+	nm_log_dbg (log_level, "   deleting route");
+
 	err = rtnl_route_del (nm_netlink_get_default_handle (), route, 0);
-	if (err < 0) {
+	if (err < 0 && (err != -ERANGE)) {
 		nm_log_err (LOGD_DEVICE,
 		            "(%s): error %d returned from rtnl_route_del(): %s",
-		            data->iface, err, nl_geterror());
+		            data->iface, err, nl_geterror ());
 	}
 }
 
 static void flush_routes (int ifindex, const char *iface, int family)
 {
 	RouteCheckData check_data;
+	guint32 log_level = LOGD_IP4 | LOGD_IP6;
+	const char *sf = "UNSPEC";
 
 	g_return_if_fail (iface != NULL);
 
@@ -1244,6 +1352,16 @@ static void flush_routes (int ifindex, const char *iface, int family)
 		}
 	}
 
+	if (family == AF_INET) {
+		log_level = LOGD_IP4;
+		sf = "INET";
+	} else if (family == AF_INET6) {
+		log_level = LOGD_IP6;
+		sf = "INET6";
+	}
+	nm_log_dbg (log_level, "(%s): flushing routes ifindex %d family %s (%d)",
+	            iface, ifindex, sf, family);
+
 	memset (&check_data, 0, sizeof (check_data));
 	check_data.iface = iface;
 	check_data.iface_idx = ifindex;