core: track IPv4 device routes in NMIP4Config

For IPv6, we create device routes when processing the RA and add it to
NMIP6Config like any other route. For IPv4 we didn't do that. Instead
we created the list of device routes during nm_ip4_config_commit() and
passed it to nm_platform_ip_route_sync().
This commit is contained in:
Thomas Haller 2017-09-12 13:45:53 +02:00
parent 4b4efac3ab
commit 9a3117f1d3
5 changed files with 139 additions and 103 deletions

View file

@ -496,8 +496,8 @@ static void nm_device_set_proxy_config (NMDevice *self, const char *pac_url);
static gboolean nm_device_set_ip4_config (NMDevice *self,
NMIP4Config *config,
guint32 default_route_metric,
gboolean commit);
gboolean commit,
GPtrArray *ip4_dev_route_blacklist);
static gboolean ip4_config_merge_and_apply (NMDevice *self,
NMIP4Config *config,
gboolean commit);
@ -3885,7 +3885,7 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config)
if (!unconfigure_ip_config)
return;
nm_device_set_ip4_config (self, NULL, 0, FALSE);
nm_device_set_ip4_config (self, NULL, FALSE, NULL);
nm_device_set_ip6_config (self, NULL, FALSE);
}
@ -5578,6 +5578,7 @@ ip4_config_merge_and_apply (NMDevice *self,
gboolean ignore_auto_dns = FALSE;
GSList *iter;
NMPlatformIP4Route default_route;
gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
/* Merge all the configs into the composite config */
if (config) {
@ -5694,12 +5695,19 @@ ip4_config_merge_and_apply (NMDevice *self,
}
END_ADD_DEFAULT_ROUTE:
if (commit) {
nm_ip4_config_add_device_routes (composite,
default_route_metric,
&ip4_dev_route_blacklist);
}
if (commit) {
if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit)
NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite);
}
success = nm_device_set_ip4_config (self, composite, default_route_metric, commit);
success = nm_device_set_ip4_config (self, composite, commit, ip4_dev_route_blacklist);
g_object_unref (composite);
if (commit)
@ -9739,8 +9747,8 @@ nm_device_get_ip4_config (NMDevice *self)
static gboolean
nm_device_set_ip4_config (NMDevice *self,
NMIP4Config *new_config,
guint32 default_route_metric,
gboolean commit)
gboolean commit,
GPtrArray *ip4_dev_route_blacklist)
{
NMDevicePrivate *priv;
NMIP4Config *old_config = NULL;
@ -9766,8 +9774,10 @@ nm_device_set_ip4_config (NMDevice *self,
if (commit && new_config) {
_commit_mtu (self, new_config);
success = nm_ip4_config_commit (new_config,
nm_device_get_platform (self),
default_route_metric);
nm_device_get_platform (self));
nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self),
nm_ip4_config_get_ifindex (new_config),
ip4_dev_route_blacklist);
}
if (new_config) {
@ -11986,7 +11996,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
/* Clean up IP configs; this does not actually deconfigure the
* interface; the caller must flush routes and addresses explicitly.
*/
nm_device_set_ip4_config (self, NULL, 0, TRUE);
nm_device_set_ip4_config (self, NULL, TRUE, NULL);
nm_device_set_ip6_config (self, NULL, TRUE);
nm_clear_nmp_object (&priv->default_route4);
nm_clear_nmp_object (&priv->default_route6);

View file

@ -107,6 +107,7 @@ dhcp4_state_changed (NMDhcpClient *client,
{
static NMIP4Config *last_config = NULL;
NMIP4Config *existing;
gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
@ -123,11 +124,17 @@ dhcp4_state_changed (NMDhcpClient *client,
nm_ip4_config_subtract (existing, last_config);
nm_ip4_config_merge (existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
nm_ip4_config_add_device_routes (existing,
global_opt.priority_v4,
&ip4_dev_route_blacklist);
if (!nm_ip4_config_commit (existing,
NM_PLATFORM_GET,
global_opt.priority_v4))
NM_PLATFORM_GET))
_LOGW (LOGD_DHCP4, "failed to apply DHCPv4 config");
nm_platform_ip4_dev_route_blacklist_set (NM_PLATFORM_GET,
gl.ifindex,
ip4_dev_route_blacklist);
if (last_config)
g_object_unref (last_config);
last_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),

View file

@ -724,23 +724,105 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i
return self;
}
gboolean
nm_ip4_config_commit (const NMIP4Config *self,
NMPlatform *platform,
guint32 default_route_metric)
void
nm_ip4_config_add_device_routes (NMIP4Config *self,
guint32 default_route_metric,
GPtrArray **out_ip4_dev_route_blacklist)
{
const NMIP4ConfigPrivate *priv;
GPtrArray *ip4_dev_route_blacklist = NULL;
const NMPlatformIP4Address *addr;
int ifindex;
NMDedupMultiIter iter;
g_return_if_fail (NM_IS_IP4_CONFIG (self));
priv = NM_IP4_CONFIG_GET_PRIVATE (self);
ifindex = nm_ip4_config_get_ifindex (self);
g_return_if_fail (ifindex > 0);
/* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config.
* As we don't do that for IPv4, add it here shortly before syncing
* the routes. */
nm_ip_config_iter_ip4_address_for_each (&iter, self, &addr) {
nm_auto_nmpobj NMPObject *r = NULL;
NMPlatformIP4Route *route;
in_addr_t network;
if (addr->plen == 0)
continue;
nm_assert (addr->plen <= 32);
/* The destination network depends on the peer-address. */
network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen);
if (_ipv4_is_zeronet (network)) {
/* Kernel doesn't add device-routes for destinations that
* start with 0.x.y.z. Skip them. */
continue;
}
r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL);
route = NMP_OBJECT_CAST_IP4_ROUTE (r);
route->ifindex = ifindex;
route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL;
route->network = network;
route->plen = addr->plen;
route->pref_src = addr->address;
route->metric = default_route_metric;
route->scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK);
nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route);
if (_lookup_route (self,
r,
NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) {
/* we already track this route. Don't add it again. */
} else
_add_route (self, nmp_object_ref (r), NULL, NULL);
if ( out_ip4_dev_route_blacklist
&& default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) {
nm_auto_nmpobj NMPObject *r_dev = NULL;
r_dev = nmp_object_clone (r, FALSE);
route = NMP_OBJECT_CAST_IP4_ROUTE (r_dev);
route->metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE;
nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route);
if (_lookup_route (self,
r_dev,
NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) {
/* 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,
g_steal_pointer (&r_dev));
}
}
}
NM_SET_OUT (out_ip4_dev_route_blacklist, ip4_dev_route_blacklist);
}
gboolean
nm_ip4_config_commit (const NMIP4Config *self,
NMPlatform *platform)
{
gs_unref_ptrarray GPtrArray *addresses = NULL;
gs_unref_ptrarray GPtrArray *routes = NULL;
gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
int ifindex;
guint i;
gboolean success = TRUE;
g_return_val_if_fail (NM_IS_IP4_CONFIG (self), FALSE);
priv = NM_IP4_CONFIG_GET_PRIVATE (self);
ifindex = nm_ip4_config_get_ifindex (self);
g_return_val_if_fail (ifindex > 0, FALSE);
@ -750,82 +832,6 @@ nm_ip4_config_commit (const NMIP4Config *self,
routes = nm_dedup_multi_objs_to_ptr_array_head (nm_ip4_config_lookup_routes (self),
NULL, NULL);
if (addresses) {
/* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config.
* As we don't do that for IPv4, add it here shortly before syncing
* the routes. */
for (i = 0; i < addresses->len; i++) {
const NMPObject *o = addresses->pdata[i];
const NMPlatformIP4Address *addr;
nm_auto_nmpobj NMPObject *r = NULL;
NMPlatformIP4Route *route;
in_addr_t network;
if (!o)
continue;
addr = NMP_OBJECT_CAST_IP4_ADDRESS (o);
if (addr->plen == 0)
continue;
nm_assert (addr->plen <= 32);
/* The destination network depends on the peer-address. */
network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen);
if (_ipv4_is_zeronet (network)) {
/* Kernel doesn't add device-routes for destinations that
* start with 0.x.y.z. Skip them. */
continue;
}
r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL);
route = NMP_OBJECT_CAST_IP4_ROUTE (r);
route->ifindex = ifindex;
route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL;
route->network = network;
route->plen = addr->plen;
route->pref_src = addr->address;
route->metric = default_route_metric;
route->scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK);
nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route);
if (_lookup_route (self,
r,
NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) {
/* we already track this route. Don't add it again. */
} else {
if (!routes)
routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref);
g_ptr_array_add (routes, (gpointer) nmp_object_ref (r));
}
if (default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) {
nm_auto_nmpobj NMPObject *r_dev = NULL;
r_dev = nmp_object_clone (r, FALSE);
route = NMP_OBJECT_CAST_IP4_ROUTE (r_dev);
route->metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE;
nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route);
if (_lookup_route (self,
r_dev,
NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) {
/* 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,
g_steal_pointer (&r_dev));
}
}
}
}
nm_platform_ip4_address_sync (platform, ifindex, addresses);
if (!nm_platform_ip_route_sync (platform,
@ -836,10 +842,6 @@ nm_ip4_config_commit (const NMIP4Config *self,
NULL))
success = FALSE;
nm_platform_ip4_dev_route_blacklist_set (platform,
ifindex,
ip4_dev_route_blacklist);
return success;
}

View file

@ -150,9 +150,14 @@ int nm_ip4_config_get_ifindex (const NMIP4Config *self);
NMDedupMultiIndex *nm_ip4_config_get_multi_idx (const NMIP4Config *self);
NMIP4Config *nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf);
void nm_ip4_config_add_device_routes (NMIP4Config *self,
guint32 default_route_metric,
GPtrArray **out_ip4_dev_route_blacklist);
gboolean nm_ip4_config_commit (const NMIP4Config *self,
NMPlatform *platform,
guint32 default_route_metric);
NMPlatform *platform);
void nm_ip4_config_merge_setting (NMIP4Config *self, NMSettingIPConfig *setting, guint32 default_route_metric);
NMSetting *nm_ip4_config_create_setting (const NMIP4Config *self);

View file

@ -121,6 +121,8 @@ typedef struct {
NMNetns *netns;
GPtrArray *ip4_dev_route_blacklist;
GDBusProxy *proxy;
GCancellable *cancellable;
GVariant *connect_hash;
@ -1147,9 +1149,11 @@ nm_vpn_connection_apply_config (NMVpnConnection *self)
if (priv->ip4_config) {
nm_assert (priv->ip_ifindex == nm_ip4_config_get_ifindex (priv->ip4_config));
if (!nm_ip4_config_commit (priv->ip4_config,
nm_netns_get_platform (priv->netns),
nm_vpn_connection_get_ip4_route_metric (self)))
nm_netns_get_platform (priv->netns)))
return FALSE;
nm_platform_ip4_dev_route_blacklist_set (nm_netns_get_platform (priv->netns),
priv->ip_ifindex,
priv->ip4_dev_route_blacklist);
}
if (priv->ip6_config) {
@ -1601,6 +1605,12 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
nm_ip4_config_add_route (config, &r, NULL);
}
g_clear_pointer (&priv->ip4_dev_route_blacklist, g_ptr_array_unref);
nm_ip4_config_add_device_routes (config,
nm_vpn_connection_get_ip4_route_metric (self),
&priv->ip4_dev_route_blacklist);
if (priv->ip4_config) {
nm_ip4_config_replace (priv->ip4_config, config, NULL);
g_object_unref (config);
@ -2704,6 +2714,8 @@ dispose (GObject *object)
g_clear_pointer (&priv->connect_hash, g_variant_unref);
g_clear_pointer (&priv->ip4_dev_route_blacklist, g_ptr_array_unref);
nm_clear_g_source (&priv->connect_timeout);
dispatcher_cleanup (self);