From 9a3117f1d3095e58859efce57ea4fb41d8fb7696 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 12 Sep 2017 13:45:53 +0200 Subject: [PATCH] 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(). --- src/devices/nm-device.c | 28 ++++-- src/nm-iface-helper.c | 11 ++- src/nm-ip4-config.c | 178 ++++++++++++++++++------------------ src/nm-ip4-config.h | 9 +- src/vpn/nm-vpn-connection.c | 16 +++- 5 files changed, 139 insertions(+), 103 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 6158cc917a..cd523b9dad 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -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); diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 9f6ba72407..ae76ee94ef 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -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), diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 07e2f2e080..58127dafab 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -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; } diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index a8e22c2e59..49f119c614 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -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); diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 9e4189feb7..517e7eb97a 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -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);