diff --git a/src/nm-netlink-utils.c b/src/nm-netlink-utils.c index 6431225821..67339581ea 100644 --- a/src/nm-netlink-utils.c +++ b/src/nm-netlink-utils.c @@ -139,14 +139,8 @@ nm_netlink_route_delete (struct rtnl_route *route) } -/** - * nm_netlink_dump_route: - * @route: the route to dump - * - * Logs the details of a route. - **/ -void -nm_netlink_dump_route (struct rtnl_route *route) +static void +dump_route (struct rtnl_route *route) { char buf6[INET6_ADDRSTRLEN]; char buf4[INET_ADDRSTRLEN]; @@ -189,3 +183,106 @@ nm_netlink_dump_route (struct rtnl_route *route) prefixlen); } + +typedef struct { + int ifindex; + int family; + int scope; + gboolean ignore_inet6_ll_mc; + const char *iface; + NlRouteForeachFunc callback; + gpointer user_data; + struct rtnl_route *out_route; +} ForeachRouteInfo; + +static void +foreach_route_cb (struct nl_object *object, void *user_data) +{ + ForeachRouteInfo *info = user_data; + struct rtnl_route *route = (struct rtnl_route *) object; + struct nl_addr *dst; + + if (info->out_route) + return; + + if (nm_logging_level_enabled (LOGL_DEBUG)) + dump_route (route); + + if ( info->ifindex >= 0 + && rtnl_route_get_oif (route) != info->ifindex) + return; + + if ( info->scope != RT_SCOPE_UNIVERSE + && rtnl_route_get_scope (route) != info->scope) + return; + + if ( info->family != AF_UNSPEC + && rtnl_route_get_family (route) != info->family) + return; + + dst = rtnl_route_get_dst (route); + + /* Check for IPv6 LL and MC routes that might need to be ignored */ + if ( (info->family == AF_INET6 || info->family == AF_UNSPEC) + && (rtnl_route_get_family (route) == AF_INET6)) { + struct in6_addr *addr = NULL; + + if (dst) + addr = nl_addr_get_binary_addr (dst); + if (addr) { + if ( IN6_IS_ADDR_LINKLOCAL (addr) + || IN6_IS_ADDR_MC_LINKLOCAL (addr) + || (IN6_IS_ADDR_MULTICAST (addr) && (nl_addr_get_prefixlen (dst) == 8))) + return; + } + } + + info->out_route = info->callback (route, dst, info->iface, info->user_data); + if (info->out_route) { + /* Ref the route so it sticks around after the cache is cleared */ + rtnl_route_get (info->out_route); + } +} + +/** + * nm_netlink_foreach_route: + * @ifindex: the interface index to filter routes for + * @family: the address family to filter routes for + * @scope: route scope, eg RT_SCOPE_LINK + * @ignore_inet6_ll_mc: if %TRUE ignore IPv6 link-local and multi-cast routes + * @callback: function called when a route matches the filter + * @user_data: data passed to @callback + * + * Filters each route in the routing table against the given @ifindex and + * @family (if given) and calls @callback for each matching route. + * + * Returns: a route if @callback returned one; the caller must dispose of the + * route using rtnl_route_put() when it is no longer required. + **/ +struct rtnl_route * +nm_netlink_foreach_route (int ifindex, + int family, + int scope, + gboolean ignore_inet6_ll_mc, + NlRouteForeachFunc callback, + gpointer user_data) +{ + struct nl_cache *cache; + ForeachRouteInfo info; + + memset (&info, 0, sizeof (info)); + info.ifindex = ifindex; + info.family = family; + info.scope = scope; + info.ignore_inet6_ll_mc = ignore_inet6_ll_mc; + info.callback = callback; + info.user_data = user_data; + info.iface = nm_netlink_index_to_iface (ifindex); + + cache = rtnl_route_alloc_cache (nm_netlink_get_default_handle ()); + g_return_val_if_fail (cache != NULL, NULL); + nl_cache_foreach (cache, foreach_route_cb, &info); + nl_cache_free (cache); + return info.out_route; +} + diff --git a/src/nm-netlink-utils.h b/src/nm-netlink-utils.h index 87531740ec..63cec4119e 100644 --- a/src/nm-netlink-utils.h +++ b/src/nm-netlink-utils.h @@ -32,6 +32,29 @@ gboolean nm_netlink_find_address (int ifindex, gboolean nm_netlink_route_delete (struct rtnl_route *route); -void nm_netlink_dump_route (struct rtnl_route *route); +/** + * NlRouteForeachFunc: + * @route: the route being processed + * @dst: the route's destination address + * @iface: the interface name of the index passed to nm_netlink_foreach_route() + * @in_family: the address family passed to nm_netlink_foreach_route() + * @user_data: the user data pointer passed to nm_netlink_foreach_route() + * + * Returns: a route to return to the caller of nm_netlink_foreach_route() which + * terminates routing table iteration, or NULL to continue iterating the + * routing table. + **/ +typedef struct rtnl_route * (*NlRouteForeachFunc) (struct rtnl_route *route, + struct nl_addr *dst, + const char *iface, + gpointer user_data); + +struct rtnl_route * nm_netlink_foreach_route (int ifindex, + int family, + int scope, + gboolean ignore_inet6_ll_mc, + NlRouteForeachFunc callback, + gpointer user_data); #endif /* NM_NETLINK_MONITOR_H */ + diff --git a/src/nm-system.c b/src/nm-system.c index a1dd691046..f64f13ba61 100644 --- a/src/nm-system.c +++ b/src/nm-system.c @@ -1231,71 +1231,19 @@ nm_system_iface_flush_addresses (int ifindex, int family) } -static void -foreach_route (void (*callback)(struct nl_object *, gpointer), - gpointer user_data) +static struct rtnl_route * +delete_one_route (struct rtnl_route *route, + struct nl_addr *dst, + const char *iface, + gpointer user_data) { - struct nl_handle *nlh; - struct nl_cache *route_cache; + guint32 log_level = GPOINTER_TO_UINT (user_data); - 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; - guint32 log_level = LOGD_IP4 | LOGD_IP6; - - if (nm_logging_level_enabled (LOGL_DEBUG)) - nm_netlink_dump_route (route); - - /* 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) - || (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"); - if (!nm_netlink_route_delete (route)) - nm_log_err (LOGD_DEVICE, "(%s): failed to delete route", data->iface); + nm_log_err (LOGD_DEVICE, "(%s): failed to delete route", iface); + + return NULL; } /** @@ -1310,7 +1258,6 @@ check_one_route (struct nl_object *object, void *user_data) gboolean nm_system_iface_flush_routes (int ifindex, int family) { - RouteCheckData check_data; guint32 log_level = LOGD_IP4 | LOGD_IP6; const char *sf = "UNSPEC"; const char *iface; @@ -1330,56 +1277,43 @@ nm_system_iface_flush_routes (int ifindex, int family) 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; - check_data.family = family; - foreach_route (check_one_route, &check_data); - + /* 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. + */ + nm_netlink_foreach_route (ifindex, family, RT_SCOPE_UNIVERSE, TRUE, delete_one_route, GUINT_TO_POINTER (log_level)); return TRUE; } -typedef struct { - struct rtnl_route *route; - NMIP4Config *config; - int ifindex; -} SetPriorityInfo; - -static void -find_route (struct nl_object *object, gpointer user_data) +static struct rtnl_route * +find_route (struct rtnl_route *route, + struct nl_addr *dst, + const char *iface, + gpointer user_data) { - struct rtnl_route *route = (struct rtnl_route *) object; - SetPriorityInfo *info = (SetPriorityInfo *) user_data; - struct nl_addr *dst; + NMIP4Config *config = user_data; struct in_addr *dst_addr; int num; int i; - if (info->route || - rtnl_route_get_oif (route) != info->ifindex || - rtnl_route_get_scope (route) != RT_SCOPE_LINK) - return; - - dst = rtnl_route_get_dst (route); - if (nl_addr_get_family (dst) != AF_INET) - return; + if (dst && (nl_addr_get_family (dst) != AF_INET)) + return NULL; + /* Find the first route that handles a subnet of at least one of the + * device's IPv4 addresses. + */ dst_addr = nl_addr_get_binary_addr (dst); - num = nm_ip4_config_get_num_addresses (info->config); + num = nm_ip4_config_get_num_addresses (config); for (i = 0; i < num; i++) { - NMIP4Address *addr = nm_ip4_config_get_address (info->config, 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 == 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; - } + if ( prefix == nl_addr_get_prefixlen (dst) + && (address & nm_utils_ip4_prefix_to_netmask (prefix)) == dst_addr->s_addr) + return route; } + return NULL; } static void @@ -1387,20 +1321,15 @@ nm_system_device_set_priority (int ifindex, NMIP4Config *config, int priority) { - SetPriorityInfo info; struct nl_handle *nlh; + struct rtnl_route *found; - info.route = NULL; - info.config = config; - info.ifindex = ifindex; - - foreach_route (find_route, &info); - if (info.route) { - nm_netlink_route_delete (info.route); - + found = nm_netlink_foreach_route (ifindex, AF_INET, RT_SCOPE_LINK, FALSE, find_route, config); + if (found) { nlh = nm_netlink_get_default_handle (); - rtnl_route_set_prio (info.route, priority); - rtnl_route_add (nlh, info.route, 0); - rtnl_route_put (info.route); + nm_netlink_route_delete (found); + rtnl_route_set_prio (found, priority); + rtnl_route_add (nlh, found, 0); + rtnl_route_put (found); } }