l3cfg: add nm_l3_config_data_get_blacklisted_ip4_routes() util

We will need to prune routes that kernel adds (and we don't want).
This commit is contained in:
Thomas Haller 2020-08-26 21:15:32 +02:00
parent 0ab341b9e6
commit e89a095673
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
3 changed files with 94 additions and 29 deletions

View file

@ -1380,16 +1380,101 @@ _data_get_direct_route_for_host (const NML3ConfigData *self,
/*****************************************************************************/
/* Kernel likes to add device routes for all addresses. Normally, we want to suppress that
* with IFA_F_NOPREFIXROUTE. But we also want to support kernels that don't support that
* flag. So, we collect here all those routes that kernel might add but we don't want.
* If the route shows up within a certain timeout of us configuring the address, we assume
* that it was (undesirably) added by kernel and we remove it.
*
* The most common reason is that for each IPv4 address we want to add a corresponding device
* route with the right ipv4.route-metric. The route that kernel adds has metric 0, so it is
* undesired.
*
* FIXME(l3cfg): implement handling blacklisted routes.
*
* For IPv6, IFA_F_NOPREFIXROUTE is supported for a longer time and we don't do such a hack.
*/
GPtrArray *
nm_l3_config_data_get_blacklisted_ip4_routes (const NML3ConfigData *self,
gboolean is_vrf)
{
gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
const NMPObject *my_addr_obj;
NMDedupMultiIter iter;
nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE));
/* For IPv6 slaac, we explicitly add the device-routes (onlink).
* As we don't do that for IPv4 and manual IPv6 addresses. Add them here
* as dependent routes. */
nm_l3_config_data_iter_obj_for_each (&iter, self, &my_addr_obj, NMP_OBJECT_TYPE_IP4_ADDRESS) {
const NMPlatformIP4Address *const my_addr = NMP_OBJECT_CAST_IP4_ADDRESS (my_addr_obj);
in_addr_t network_4;
NMPlatformIPXRoute rx;
if (my_addr->external)
continue;
nm_assert (my_addr->plen <= 32);
if (my_addr->plen == 0)
continue;
network_4 = nm_utils_ip4_address_clear_host_address (my_addr->peer_address,
my_addr->plen);
if (nm_utils_ip4_address_is_zeronet (network_4)) {
/* Kernel doesn't add device-routes for destinations that
* start with 0.x.y.z. Skip them. */
continue;
}
if ( my_addr->plen == 32
&& my_addr->address == my_addr->peer_address) {
/* Kernel doesn't add device-routes for /32 addresses unless
* they have a peer. */
continue;
}
rx.r4 = (NMPlatformIP4Route) {
.ifindex = self->ifindex,
.rt_source = NM_IP_CONFIG_SOURCE_KERNEL,
.network = network_4,
.plen = my_addr->plen,
.pref_src = my_addr->address,
.table_coerced = nm_platform_route_table_coerce (RT_TABLE_MAIN),
.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE,
.scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK),
};
nm_platform_ip_route_normalize (AF_INET, &rx.rx);
if (nm_l3_config_data_lookup_route (self,
AF_INET,
&rx.rx)) {
/* we track such a route explicitly. Don't blacklist it. */
continue;
}
if (!ip4_dev_route_blacklist)
ip4_dev_route_blacklist = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref);
g_ptr_array_add (ip4_dev_route_blacklist,
nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, &rx));
}
return g_steal_pointer (&ip4_dev_route_blacklist);
}
/*****************************************************************************/
void
nm_l3_config_data_add_dependent_routes (NML3ConfigData *self,
int addr_family,
guint32 route_table,
guint32 route_metric,
gboolean is_vrf,
GPtrArray **out_ip4_dev_route_blacklist)
gboolean is_vrf)
{
const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family);
gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
gs_unref_ptrarray GPtrArray *extra_onlink_routes = NULL;
const NMPObject *my_addr_obj;
const NMPObject *my_route_obj;
@ -1490,28 +1575,6 @@ nm_l3_config_data_add_dependent_routes (NML3ConfigData *self,
};
nm_platform_ip_route_normalize (addr_family, &rx.rx);
nm_l3_config_data_add_route (self, addr_family, NULL, &rx.rx);
if ( IS_IPv4
&& out_ip4_dev_route_blacklist
&& ( route_table != RT_TABLE_MAIN
|| route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE)) {
rx.r4.table_coerced = nm_platform_route_table_coerce (RT_TABLE_MAIN);
rx.r4.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE;
nm_platform_ip_route_normalize (addr_family, &rx.rx);
if (nm_l3_config_data_lookup_route (self,
addr_family,
&rx.rx)) {
/* we track such a route explicitly. Don't blacklist it. */
} else {
if (!ip4_dev_route_blacklist)
ip4_dev_route_blacklist = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref);
g_ptr_array_add (ip4_dev_route_blacklist,
nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, &rx));
}
}
} else {
const gboolean has_peer = !IN6_IS_ADDR_UNSPECIFIED (&my_addr->a6.peer_address);
int routes_i;
@ -1602,8 +1665,6 @@ nm_l3_config_data_add_dependent_routes (NML3ConfigData *self,
NULL);
}
}
NM_SET_OUT (out_ip4_dev_route_blacklist, g_steal_pointer (&ip4_dev_route_blacklist));
}
/*****************************************************************************/

View file

@ -105,12 +105,14 @@ void nm_l3_config_data_merge (NML3ConfigData *self,
NML3ConfigMergeHookAddObj hook_add_addr,
gpointer hook_user_data);
GPtrArray *nm_l3_config_data_get_blacklisted_ip4_routes (const NML3ConfigData *self,
gboolean is_vrf);
void nm_l3_config_data_add_dependent_routes (NML3ConfigData *self,
int addr_family,
guint32 route_table,
guint32 route_metric,
gboolean is_vrf,
GPtrArray **out_ip4_dev_route_blacklist);
gboolean is_vrf);
/*****************************************************************************/

View file

@ -309,6 +309,8 @@ typedef enum {
\
guint8 plen; \
\
/* FIXME(l3cfg): the external marker won't be necessary anymore, because we only
* merge addresses we care about, and ignore (don't remove) external addresses. */ \
bool external:1; \
\
bool use_ip4_broadcast_address:1; \