mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-05 02:10:17 +01:00
policy: merge branch 'th/bgo735512_route_metric_v2'
Further fixes to NMDefaultRouteManager. https://bugzilla.gnome.org/show_bug.cgi?id=735512
This commit is contained in:
commit
5338178d81
6 changed files with 602 additions and 190 deletions
|
|
@ -238,11 +238,14 @@ typedef struct {
|
|||
IpState ip4_state;
|
||||
NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */
|
||||
NMIP4Config * ext_ip4_config; /* Stuff added outside NM */
|
||||
gboolean ext_ip4_config_had_any_addresses;
|
||||
NMIP4Config * wwan_ip4_config; /* WWAN configuration */
|
||||
struct {
|
||||
gboolean v4_has;
|
||||
gboolean v4_is_assumed;
|
||||
NMPlatformIP4Route v4;
|
||||
gboolean v6_has;
|
||||
gboolean v6_is_assumed;
|
||||
NMPlatformIP6Route v6;
|
||||
} default_route;
|
||||
|
||||
|
|
@ -273,6 +276,7 @@ typedef struct {
|
|||
NMIP6Config * vpn6_config; /* routes added by a VPN which uses this device */
|
||||
NMIP6Config * wwan_ip6_config;
|
||||
NMIP6Config * ext_ip6_config; /* Stuff added outside NM */
|
||||
gboolean ext_ip6_config_had_any_addresses;
|
||||
gboolean nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */
|
||||
|
||||
NMRDisc * rdisc;
|
||||
|
|
@ -724,7 +728,7 @@ nm_device_get_ip6_route_metric (NMDevice *self)
|
|||
}
|
||||
|
||||
const NMPlatformIP4Route *
|
||||
nm_device_get_ip4_default_route (NMDevice *self)
|
||||
nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed)
|
||||
{
|
||||
NMDevicePrivate *priv;
|
||||
|
||||
|
|
@ -732,11 +736,14 @@ nm_device_get_ip4_default_route (NMDevice *self)
|
|||
|
||||
priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
if (out_is_assumed)
|
||||
*out_is_assumed = priv->default_route.v4_is_assumed;
|
||||
|
||||
return priv->default_route.v4_has ? &priv->default_route.v4 : NULL;
|
||||
}
|
||||
|
||||
const NMPlatformIP6Route *
|
||||
nm_device_get_ip6_default_route (NMDevice *self)
|
||||
nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed)
|
||||
{
|
||||
NMDevicePrivate *priv;
|
||||
|
||||
|
|
@ -744,6 +751,9 @@ nm_device_get_ip6_default_route (NMDevice *self)
|
|||
|
||||
priv = NM_DEVICE_GET_PRIVATE (self);
|
||||
|
||||
if (out_is_assumed)
|
||||
*out_is_assumed = priv->default_route.v6_is_assumed;
|
||||
|
||||
return priv->default_route.v6_has ? &priv->default_route.v6 : NULL;
|
||||
}
|
||||
|
||||
|
|
@ -2782,8 +2792,10 @@ ip4_config_merge_and_apply (NMDevice *self,
|
|||
*/
|
||||
connection = nm_device_get_connection (self);
|
||||
priv->default_route.v4_has = FALSE;
|
||||
priv->default_route.v4_is_assumed = TRUE;
|
||||
if (connection) {
|
||||
gboolean assumed = nm_device_uses_assumed_connection (self);
|
||||
NMPlatformIP4Route *route = &priv->default_route.v4;
|
||||
|
||||
if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) {
|
||||
nm_ip4_config_merge_setting (composite,
|
||||
|
|
@ -2802,13 +2814,17 @@ ip4_config_merge_and_apply (NMDevice *self,
|
|||
* NMDefaultRouteManager eventually configures (because the it might
|
||||
* tweak the effective metric).
|
||||
*/
|
||||
if (nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), connection)) {
|
||||
if ( !assumed
|
||||
&& nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), connection)) {
|
||||
guint32 gateway = 0;
|
||||
NMPlatformIP4Route *route = &priv->default_route.v4;
|
||||
|
||||
if (assumed)
|
||||
priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) route);
|
||||
else {
|
||||
priv->default_route.v4_is_assumed = FALSE;
|
||||
if ( (!commit && priv->ext_ip4_config_had_any_addresses)
|
||||
|| ( commit && nm_ip4_config_get_num_addresses (composite))) {
|
||||
/* For managed interfaces, we can only configure a gateway, if either the external config indicates
|
||||
* that we already have addresses, or if we are about to commit any addresses.
|
||||
* Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any
|
||||
* addresses for the route. */
|
||||
gateway = nm_ip4_config_get_gateway (composite);
|
||||
if ( gateway
|
||||
|| nm_device_get_device_type (self) == NM_DEVICE_TYPE_MODEM) {
|
||||
|
|
@ -2832,6 +2848,10 @@ ip4_config_merge_and_apply (NMDevice *self,
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* For interfaces that are assumed and that have no default-route by configuration, we assume
|
||||
* the default connection and pick up whatever is configured. */
|
||||
priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) route);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3331,8 +3351,10 @@ ip6_config_merge_and_apply (NMDevice *self,
|
|||
*/
|
||||
connection = nm_device_get_connection (self);
|
||||
priv->default_route.v6_has = FALSE;
|
||||
priv->default_route.v6_is_assumed = TRUE;
|
||||
if (connection) {
|
||||
gboolean assumed = nm_device_uses_assumed_connection (self);
|
||||
NMPlatformIP6Route *route = &priv->default_route.v6;
|
||||
|
||||
if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) {
|
||||
nm_ip6_config_merge_setting (composite,
|
||||
|
|
@ -3351,13 +3373,17 @@ ip6_config_merge_and_apply (NMDevice *self,
|
|||
* NMDefaultRouteManager eventually configures (because the it might
|
||||
* tweak the effective metric).
|
||||
*/
|
||||
if (nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), connection)) {
|
||||
if ( !assumed
|
||||
&& nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), connection)) {
|
||||
const struct in6_addr *gateway = NULL;
|
||||
NMPlatformIP6Route *route = &priv->default_route.v6;
|
||||
|
||||
if (assumed)
|
||||
priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) route);
|
||||
else {
|
||||
priv->default_route.v6_is_assumed = FALSE;
|
||||
if ( (!commit && priv->ext_ip6_config_had_any_addresses)
|
||||
|| ( commit && nm_ip6_config_get_num_addresses (composite))) {
|
||||
/* For managed interfaces, we can only configure a gateway, if either the external config indicates
|
||||
* that we already have addresses, or if we are about to commit any addresses.
|
||||
* Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any
|
||||
* addresses for the route. */
|
||||
gateway = nm_ip6_config_get_gateway (composite);
|
||||
if (gateway) {
|
||||
memset (route, 0, sizeof (*route));
|
||||
|
|
@ -3380,6 +3406,10 @@ ip6_config_merge_and_apply (NMDevice *self,
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* For interfaces that are assumed and that have no default-route by configuration, we assume
|
||||
* the default connection and pick up whatever is configured. */
|
||||
priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) route);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6336,7 +6366,8 @@ update_ip_config (NMDevice *self, gboolean initial)
|
|||
/* IPv4 */
|
||||
g_clear_object (&priv->ext_ip4_config);
|
||||
priv->ext_ip4_config = nm_ip4_config_capture (ifindex, capture_resolv_conf);
|
||||
|
||||
priv->ext_ip4_config_had_any_addresses = ( priv->ext_ip4_config
|
||||
&& nm_ip4_config_get_num_addresses (priv->ext_ip4_config) > 0);
|
||||
if (priv->ext_ip4_config) {
|
||||
if (initial) {
|
||||
g_clear_object (&priv->dev_ip4_config);
|
||||
|
|
@ -6355,6 +6386,8 @@ update_ip_config (NMDevice *self, gboolean initial)
|
|||
/* IPv6 */
|
||||
g_clear_object (&priv->ext_ip6_config);
|
||||
priv->ext_ip6_config = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
|
||||
priv->ext_ip6_config_had_any_addresses = ( priv->ext_ip6_config
|
||||
&& nm_ip6_config_get_num_addresses (priv->ext_ip6_config) > 0);
|
||||
if (priv->ext_ip6_config) {
|
||||
|
||||
/* Check this before modifying ext_ip6_config */
|
||||
|
|
@ -6933,10 +6966,12 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure)
|
|||
NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
|
||||
|
||||
priv->default_route.v4_has = FALSE;
|
||||
priv->default_route.v4_is_assumed = TRUE;
|
||||
priv->default_route.v6_has = FALSE;
|
||||
priv->default_route.v6_is_assumed = TRUE;
|
||||
|
||||
nm_default_route_manager_ip4_remove_default_route (nm_default_route_manager_get (), self);
|
||||
nm_default_route_manager_ip6_remove_default_route (nm_default_route_manager_get (), self);
|
||||
nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
|
||||
nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
|
||||
|
||||
/* Clean up IP configs; this does not actually deconfigure the
|
||||
* interface; the caller must flush routes and addresses explicitly.
|
||||
|
|
@ -6954,6 +6989,9 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure)
|
|||
g_clear_object (&priv->wwan_ip6_config);
|
||||
g_clear_object (&priv->ip6_config);
|
||||
|
||||
priv->ext_ip4_config_had_any_addresses = FALSE;
|
||||
priv->ext_ip6_config_had_any_addresses = FALSE;
|
||||
|
||||
clear_act_request (self);
|
||||
|
||||
/* Clear legacy IPv4 address property */
|
||||
|
|
@ -7730,6 +7768,9 @@ nm_device_init (NMDevice *self)
|
|||
priv->unmanaged_flags = NM_UNMANAGED_INTERNAL;
|
||||
priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
|
||||
priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
|
||||
|
||||
priv->default_route.v4_is_assumed = TRUE;
|
||||
priv->default_route.v6_is_assumed = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -362,8 +362,8 @@ gboolean nm_device_owns_iface (NMDevice *device, const char *iface);
|
|||
|
||||
NMConnection *nm_device_new_default_connection (NMDevice *self);
|
||||
|
||||
const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self);
|
||||
const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self);
|
||||
const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed);
|
||||
const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed);
|
||||
|
||||
void nm_device_spawn_iface_helper (NMDevice *self);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@
|
|||
typedef struct {
|
||||
GPtrArray *entries_ip4;
|
||||
GPtrArray *entries_ip6;
|
||||
struct {
|
||||
guint guard;
|
||||
guint backoff_wait_time_ms;
|
||||
guint idle_handle;
|
||||
gboolean has_v4_changes;
|
||||
gboolean has_v6_changes;
|
||||
} resync;
|
||||
} NMDefaultRouteManagerPrivate;
|
||||
|
||||
#define NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerPrivate))
|
||||
|
|
@ -51,7 +58,7 @@ static NMDefaultRouteManager *_instance;
|
|||
guint64 __domain = __addr_family == AF_INET ? LOGD_IP4 : LOGD_IP6; \
|
||||
\
|
||||
if (nm_logging_enabled ((level), (__domain))) { \
|
||||
char __ch = __addr_family == AF_INET ? '4' : '6'; \
|
||||
char __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \
|
||||
char __prefix[30] = "default-route"; \
|
||||
\
|
||||
if ((self) != _instance) \
|
||||
|
|
@ -69,12 +76,18 @@ static NMDefaultRouteManager *_instance;
|
|||
#define _LOGW(addr_family, ...) _LOG (LOGL_WARN , addr_family, __VA_ARGS__)
|
||||
#define _LOGE(addr_family, ...) _LOG (LOGL_ERR , addr_family, __VA_ARGS__)
|
||||
|
||||
#define LOG_ENTRY_FMT "entry[%u/%s:%p:%s]"
|
||||
#define LOG_ENTRY_FMT "entry[%u/%s:%p:%s:%c%c]"
|
||||
#define LOG_ENTRY_ARGS(entry_idx, entry) \
|
||||
entry_idx, \
|
||||
NM_IS_DEVICE (entry->source.pointer) ? "dev" : "vpn", \
|
||||
entry->source.pointer, \
|
||||
NM_IS_DEVICE (entry->source.pointer) ? nm_device_get_iface (entry->source.device) : nm_vpn_connection_get_connection_id (entry->source.vpn)
|
||||
(entry_idx), \
|
||||
NM_IS_DEVICE ((entry)->source.pointer) ? "dev" : "vpn", \
|
||||
(entry)->source.pointer, \
|
||||
NM_IS_DEVICE ((entry)->source.pointer) ? nm_device_get_iface ((entry)->source.device) : nm_vpn_connection_get_connection_id ((entry)->source.vpn), \
|
||||
((entry)->never_default ? 'N' : 'n'), \
|
||||
((entry)->synced ? 'S' : 's')
|
||||
|
||||
/***********************************************************************************/
|
||||
|
||||
static void _resync_idle_cancel (NMDefaultRouteManager *self);
|
||||
|
||||
/***********************************************************************************/
|
||||
|
||||
|
|
@ -85,16 +98,33 @@ typedef struct {
|
|||
NMDevice *device;
|
||||
NMVpnConnection *vpn;
|
||||
} source;
|
||||
union {
|
||||
NMPlatformIPRoute route;
|
||||
NMPlatformIP4Route route4;
|
||||
NMPlatformIP6Route route6;
|
||||
};
|
||||
gboolean synced; /* if true, we synced the entry to platform. We don't sync assumed devices */
|
||||
NMPlatformIPXRoute route;
|
||||
|
||||
/* it makes sense to order sources based on their priority, without
|
||||
* actually adding a default route. This is useful to decide which
|
||||
* DNS server to prefer. never_default entries are not synced to platform. */
|
||||
/* Whether the route is synced to platform and has a default route.
|
||||
*
|
||||
* ( synced && !never_default): the interface gets a default route that
|
||||
* is enforced and managed by NMDefaultRouteManager.
|
||||
*
|
||||
* (!synced && !never_default): the interface has this route, but it is assumed.
|
||||
* Assumed interfaces are those that have no tracked entry or that only have
|
||||
* (!synced && !never_default) entries. NMDefaultRouteManager will not touch
|
||||
* default routes on these interfaces.
|
||||
* This combination makes only sense for device sources.
|
||||
* They are tracked so that assumed devices can also be the best device.
|
||||
*
|
||||
* ( synced && never_default): entries of this kind are a placeholder
|
||||
* to indicate that the ifindex is managed but has no default-route.
|
||||
* Missing entries also indicate that a certain ifindex has no default-route.
|
||||
* The difference is that missing entries are considered assumed while on
|
||||
* (synced && never_default) entires the absence of the default route
|
||||
* is enforced. NMDefaultRouteManager will actively remove any default
|
||||
* route on such ifindexes.
|
||||
* Also, for VPN sources in addition we track them so that a never-default
|
||||
* VPN connection can be choosen by get_best_config() to receive the DNS configuration.
|
||||
*
|
||||
* (!synced && never_default): this combination makes no sense.
|
||||
*/
|
||||
gboolean synced;
|
||||
gboolean never_default;
|
||||
|
||||
guint32 effective_metric;
|
||||
|
|
@ -104,6 +134,7 @@ typedef struct {
|
|||
int addr_family;
|
||||
GPtrArray *(*get_entries) (NMDefaultRouteManagerPrivate *priv);
|
||||
const char *(*platform_route_to_string) (const NMPlatformIPRoute *route);
|
||||
GArray *(*platform_route_get_all) (int ifindex, NMPlatformGetRouteMode mode);
|
||||
gboolean (*platform_route_delete_default) (int ifindex, guint32 metric);
|
||||
guint32 (*route_metric_normalize) (guint32 metric);
|
||||
} VTableIP;
|
||||
|
|
@ -112,6 +143,43 @@ static const VTableIP vtable_ip4, vtable_ip6;
|
|||
|
||||
#define VTABLE_IS_IP4 (vtable->addr_family == AF_INET)
|
||||
|
||||
static NMPlatformIPRoute *
|
||||
_vt_route_index (const VTableIP *vtable, GArray *routes, guint index)
|
||||
{
|
||||
if (VTABLE_IS_IP4)
|
||||
return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, index);
|
||||
else
|
||||
return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, index);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_vt_routes_has_entry (const VTableIP *vtable, GArray *routes, const Entry *entry)
|
||||
{
|
||||
guint i;
|
||||
NMPlatformIPXRoute route = entry->route;
|
||||
|
||||
route.rx.metric = entry->effective_metric;
|
||||
|
||||
if (VTABLE_IS_IP4) {
|
||||
for (i = 0; i < routes->len; i++) {
|
||||
NMPlatformIP4Route *r = &g_array_index (routes, NMPlatformIP4Route, i);
|
||||
|
||||
route.rx.source = r->source;
|
||||
if (nm_platform_ip4_route_cmp (r, &route.r4) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < routes->len; i++) {
|
||||
NMPlatformIP6Route *r = &g_array_index (routes, NMPlatformIP6Route, i);
|
||||
|
||||
route.rx.source = r->source;
|
||||
if (nm_platform_ip6_route_cmp (r, &route.r6) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
_entry_free (Entry *entry)
|
||||
{
|
||||
|
|
@ -141,13 +209,13 @@ _entry_find_by_source (GPtrArray *entries, gpointer source, guint *out_idx)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
static gboolean
|
||||
_platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, guint32 metric)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
GPtrArray *entries = vtable->get_entries (priv);
|
||||
guint i;
|
||||
gboolean has_unsynced_entry = FALSE;
|
||||
Entry *entry_unsynced = NULL;
|
||||
Entry *entry = NULL;
|
||||
gboolean success;
|
||||
|
||||
|
|
@ -165,75 +233,76 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
|
|||
|
||||
if (e->synced) {
|
||||
g_assert (!entry || metric == G_MAXUINT32);
|
||||
entry = e;
|
||||
if (!entry)
|
||||
entry = e;
|
||||
} else
|
||||
has_unsynced_entry = TRUE;
|
||||
entry_unsynced = e;
|
||||
}
|
||||
|
||||
/* For synced entries, we expect that the metric is chosen uniquely. */
|
||||
g_assert (!entry || !has_unsynced_entry || metric == G_MAXUINT32);
|
||||
/* We don't expect to have an unsynced *and* a synced entry for the same metric.
|
||||
* Unless, (a) their metric is G_MAXUINT32, in which case we could not find an unused effective metric,
|
||||
* or (b) if we have an unsynced and a synced entry for the same ifindex.
|
||||
* The latter case happens for example when activating an openvpn connection (synced) and
|
||||
* assuming the corresponding tun0 interface (unsynced). */
|
||||
g_assert (!entry || !entry_unsynced || (entry->route.rx.ifindex == entry_unsynced->route.rx.ifindex) || metric == G_MAXUINT32);
|
||||
|
||||
/* we only add the route, if we have an (to be synced) entry for it. */
|
||||
if (!entry)
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
if (VTABLE_IS_IP4) {
|
||||
success = nm_platform_ip4_route_add (entry->route.ifindex,
|
||||
entry->route.source,
|
||||
success = nm_platform_ip4_route_add (entry->route.rx.ifindex,
|
||||
entry->route.rx.source,
|
||||
0,
|
||||
0,
|
||||
entry->route4.gateway,
|
||||
entry->route.r4.gateway,
|
||||
entry->effective_metric,
|
||||
entry->route.mss);
|
||||
entry->route.rx.mss);
|
||||
} else {
|
||||
success = nm_platform_ip6_route_add (entry->route.ifindex,
|
||||
entry->route.source,
|
||||
success = nm_platform_ip6_route_add (entry->route.rx.ifindex,
|
||||
entry->route.rx.source,
|
||||
in6addr_any,
|
||||
0,
|
||||
entry->route6.gateway,
|
||||
entry->route.r6.gateway,
|
||||
entry->effective_metric,
|
||||
entry->route.mss);
|
||||
entry->route.rx.mss);
|
||||
}
|
||||
if (!success)
|
||||
_LOGW (vtable->addr_family, "failed to add default route %s with effective metric %u", vtable->platform_route_to_string (&entry->route), (guint) entry->effective_metric);
|
||||
if (!success) {
|
||||
_LOGW (vtable->addr_family, "failed to add default route %s with effective metric %u",
|
||||
vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self)
|
||||
static gboolean
|
||||
_platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, int ifindex_to_flush)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
GPtrArray *entries = vtable->get_entries (priv);
|
||||
GArray *routes;
|
||||
guint i, j;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
/* prune all other default routes from this device. */
|
||||
if (VTABLE_IS_IP4)
|
||||
routes = nm_platform_ip4_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT);
|
||||
else
|
||||
routes = nm_platform_ip6_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT);
|
||||
routes = vtable->platform_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT);
|
||||
|
||||
for (i = 0; i < routes->len; i++) {
|
||||
const NMPlatformIPRoute *route;
|
||||
gboolean has_ifindex_synced = FALSE;
|
||||
Entry *entry = NULL;
|
||||
|
||||
if (VTABLE_IS_IP4)
|
||||
route = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, i);
|
||||
else
|
||||
route = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, i);
|
||||
route = _vt_route_index (vtable, routes, i);
|
||||
|
||||
/* look at all entires and see if the route for this ifindex pair is
|
||||
* a known entry. */
|
||||
for (j = 0; j < entries->len; j++) {
|
||||
Entry *e = g_ptr_array_index (entries, j);
|
||||
|
||||
if (e->never_default)
|
||||
continue;
|
||||
|
||||
if ( e->route.ifindex == route->ifindex
|
||||
if ( e->route.rx.ifindex == route->ifindex
|
||||
&& e->synced) {
|
||||
has_ifindex_synced = TRUE;
|
||||
if (e->effective_metric == route->metric)
|
||||
if ( !e->never_default
|
||||
&& e->effective_metric == route->metric)
|
||||
entry = e;
|
||||
}
|
||||
}
|
||||
|
|
@ -245,10 +314,14 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self)
|
|||
* Otherwise, don't delete the route because it's configured
|
||||
* externally (and will be assumed -- or already is assumed).
|
||||
*/
|
||||
if (has_ifindex_synced && !entry)
|
||||
if ( !entry
|
||||
&& (has_ifindex_synced || ifindex_to_flush == route->ifindex)) {
|
||||
vtable->platform_route_delete_default (route->ifindex, route->metric);
|
||||
changed = TRUE;
|
||||
}
|
||||
}
|
||||
g_array_free (routes, TRUE);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -259,8 +332,8 @@ _sort_entries_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
|
|||
const Entry *e_b = *((const Entry **) b);
|
||||
|
||||
/* when comparing routes, we consider the (original) metric. */
|
||||
m_a = e_a->route.metric;
|
||||
m_b = e_b->route.metric;
|
||||
m_a = e_a->route.rx.metric;
|
||||
m_b = e_b->route.rx.metric;
|
||||
|
||||
/* we normalize route.metric already in _ipx_update_default_route().
|
||||
* so we can just compare the metrics numerically */
|
||||
|
|
@ -293,24 +366,93 @@ _sort_entries_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
_resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *changed_entry, const Entry *old_entry, gboolean do_sync)
|
||||
static GHashTable *
|
||||
_get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *self, GArray *routes)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
Entry *entry;
|
||||
guint i;
|
||||
gint64 last_metric = -1;
|
||||
guint32 expected_metric;
|
||||
GPtrArray *entries;
|
||||
GHashTableIter iter;
|
||||
gpointer ptr;
|
||||
GHashTable *changed_metrics = g_hash_table_new (NULL, NULL);
|
||||
guint i, j;
|
||||
GHashTable *result;
|
||||
|
||||
/* create a list of all metrics that are currently assigned on an interface
|
||||
* that is *not* already covered by one of our synced entries.
|
||||
* IOW, returns the metrics that are in use by assumed interfaces
|
||||
* that we want to preserve. */
|
||||
|
||||
entries = vtable->get_entries (priv);
|
||||
|
||||
if (old_entry && old_entry->synced) {
|
||||
result = g_hash_table_new (NULL, NULL);
|
||||
|
||||
for (i = 0; i < routes->len; i++) {
|
||||
gboolean ifindex_has_synced_entry = FALSE;
|
||||
const NMPlatformIPRoute *route;
|
||||
|
||||
route = _vt_route_index (vtable, routes, i);
|
||||
|
||||
for (j = 0; j < entries->len; j++) {
|
||||
Entry *e = g_ptr_array_index (entries, j);
|
||||
|
||||
if ( e->synced
|
||||
&& e->route.rx.ifindex == route->ifindex) {
|
||||
ifindex_has_synced_entry = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ifindex_has_synced_entry)
|
||||
g_hash_table_add (result, GUINT_TO_POINTER (vtable->route_metric_normalize (route->metric)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
_sort_metrics_ascending_fcn (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
guint32 m_a = *((guint32 *) a);
|
||||
guint32 m_b = *((guint32 *) b);
|
||||
|
||||
if (m_a < m_b)
|
||||
return -1;
|
||||
return m_a == m_b ? 0 : 1;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *changed_entry, const Entry *old_entry, gboolean external_change)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
Entry *entry;
|
||||
guint i, j;
|
||||
gint64 last_metric = -1;
|
||||
guint32 expected_metric;
|
||||
GPtrArray *entries;
|
||||
GArray *changed_metrics = g_array_new (FALSE, FALSE, sizeof (guint32));
|
||||
GHashTable *assumed_metrics;
|
||||
GArray *routes;
|
||||
gboolean changed = FALSE;
|
||||
int ifindex_to_flush = 0;
|
||||
|
||||
g_assert (priv->resync.guard == 0);
|
||||
priv->resync.guard++;
|
||||
|
||||
if (!external_change) {
|
||||
if (VTABLE_IS_IP4)
|
||||
priv->resync.has_v4_changes = FALSE;
|
||||
else
|
||||
priv->resync.has_v6_changes = FALSE;
|
||||
if (!priv->resync.has_v4_changes && !priv->resync.has_v6_changes)
|
||||
_resync_idle_cancel (self);
|
||||
}
|
||||
|
||||
entries = vtable->get_entries (priv);
|
||||
|
||||
routes = vtable->platform_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT);
|
||||
|
||||
assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes);
|
||||
|
||||
if (old_entry && old_entry->synced && !old_entry->never_default) {
|
||||
/* The old version obviously changed. */
|
||||
g_hash_table_add (changed_metrics, GUINT_TO_POINTER (old_entry->effective_metric));
|
||||
g_array_append_val (changed_metrics, old_entry->effective_metric);
|
||||
}
|
||||
|
||||
/* first iterate over all entries and adjust the effective metrics. */
|
||||
|
|
@ -323,44 +465,122 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
|
|||
continue;
|
||||
|
||||
if (!entry->synced) {
|
||||
last_metric = MAX (last_metric, (gint64) entry->effective_metric);
|
||||
gboolean has_synced_entry = FALSE;
|
||||
|
||||
/* A non synced entry is completely ignored, if we have
|
||||
* a synced entry for the same if index.
|
||||
* Otherwise the metric of the entry is still remembered as
|
||||
* last_metric to avoid reusing it. */
|
||||
for (j = 0; j < entries->len; j++) {
|
||||
const Entry *e = g_ptr_array_index (entries, j);
|
||||
|
||||
if ( e->synced
|
||||
&& e->route.rx.ifindex == entry->route.rx.ifindex) {
|
||||
has_synced_entry = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_synced_entry)
|
||||
last_metric = MAX (last_metric, (gint64) entry->effective_metric);
|
||||
continue;
|
||||
}
|
||||
|
||||
expected_metric = entry->route.metric;
|
||||
expected_metric = entry->route.rx.metric;
|
||||
if ((gint64) expected_metric <= last_metric)
|
||||
expected_metric = last_metric == G_MAXUINT32 ? G_MAXUINT32 : last_metric + 1;
|
||||
|
||||
while ( expected_metric < G_MAXUINT32
|
||||
&& g_hash_table_contains (assumed_metrics, GUINT_TO_POINTER (expected_metric))) {
|
||||
gboolean has_metric_for_ifindex = FALSE;
|
||||
|
||||
/* Check if there are assumed devices that have default routes with this metric.
|
||||
* If there are any, we have to pick another effective_metric. */
|
||||
|
||||
/* However, if there is a matching route (ifindex+metric) for our current entry, we are done. */
|
||||
for (j = 0; j < routes->len; j++) {
|
||||
const NMPlatformIPRoute *r = _vt_route_index (vtable, routes, i);
|
||||
|
||||
if ( r->metric == expected_metric
|
||||
&& r->ifindex == entry->route.rx.ifindex) {
|
||||
has_metric_for_ifindex = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_metric_for_ifindex)
|
||||
break;
|
||||
expected_metric++;
|
||||
}
|
||||
|
||||
if (changed_entry == entry) {
|
||||
/* for the changed entry, the previous metric was either old_entry->effective_metric,
|
||||
* or none. Hence, we only have to remember what is going to change. */
|
||||
g_hash_table_add (changed_metrics, GUINT_TO_POINTER (expected_metric));
|
||||
if (old_entry)
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": update %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), vtable->platform_route_to_string (&entry->route), (guint) old_entry->effective_metric, (guint) expected_metric);
|
||||
else
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": add %s (%u)", LOG_ENTRY_ARGS (i, entry), vtable->platform_route_to_string (&entry->route), (guint) expected_metric);
|
||||
g_array_append_val (changed_metrics, expected_metric);
|
||||
if (old_entry) {
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": update %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry),
|
||||
vtable->platform_route_to_string (&entry->route.rx), (guint) old_entry->effective_metric,
|
||||
(guint) expected_metric);
|
||||
} else {
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": add %s (%u)", LOG_ENTRY_ARGS (i, entry),
|
||||
vtable->platform_route_to_string (&entry->route.rx), (guint) expected_metric);
|
||||
}
|
||||
} else if (entry->effective_metric != expected_metric) {
|
||||
g_hash_table_add (changed_metrics, GUINT_TO_POINTER (entry->effective_metric));
|
||||
g_hash_table_add (changed_metrics, GUINT_TO_POINTER (expected_metric));
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": resync metric %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), vtable->platform_route_to_string (&entry->route), (guint) entry->effective_metric, (guint) expected_metric);
|
||||
g_array_append_val (changed_metrics, entry->effective_metric);
|
||||
g_array_append_val (changed_metrics, expected_metric);
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": resync metric %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry),
|
||||
vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric,
|
||||
(guint) expected_metric);
|
||||
} else {
|
||||
if (!_vt_routes_has_entry (vtable, routes, entry)) {
|
||||
g_array_append_val (changed_metrics, entry->effective_metric);
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": readd route %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry),
|
||||
vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric,
|
||||
(guint) entry->effective_metric);
|
||||
}
|
||||
}
|
||||
|
||||
entry->effective_metric = expected_metric;
|
||||
if (entry->effective_metric != expected_metric) {
|
||||
entry->effective_metric = expected_metric;
|
||||
changed = TRUE;
|
||||
}
|
||||
last_metric = expected_metric;
|
||||
}
|
||||
|
||||
if (do_sync) {
|
||||
g_hash_table_iter_init (&iter, changed_metrics);
|
||||
while (g_hash_table_iter_next (&iter, &ptr, NULL))
|
||||
_platform_route_sync_add (vtable, self, GPOINTER_TO_UINT (ptr));
|
||||
_platform_route_sync_flush (vtable, self);
|
||||
g_array_free (routes, TRUE);
|
||||
|
||||
g_array_sort (changed_metrics, _sort_metrics_ascending_fcn);
|
||||
last_metric = -1;
|
||||
for (j = 0; j < changed_metrics->len; j++) {
|
||||
expected_metric = g_array_index (changed_metrics, guint32, j);
|
||||
|
||||
if (last_metric == (gint64) expected_metric) {
|
||||
/* skip duplicates. */
|
||||
continue;
|
||||
}
|
||||
changed |= _platform_route_sync_add (vtable, self, expected_metric);
|
||||
last_metric = expected_metric;
|
||||
}
|
||||
|
||||
g_hash_table_unref (changed_metrics);
|
||||
if ( old_entry
|
||||
&& !changed_entry
|
||||
&& old_entry->synced
|
||||
&& !old_entry->never_default) {
|
||||
/* If we entriely remove an entry that was synced before, we must make
|
||||
* sure to flush routes for this ifindex too. Otherwise they linger
|
||||
* around as "assumed" routes */
|
||||
ifindex_to_flush = old_entry->route.rx.ifindex;
|
||||
}
|
||||
|
||||
changed |= _platform_route_sync_flush (vtable, self, ifindex_to_flush);
|
||||
|
||||
g_array_free (changed_metrics, TRUE);
|
||||
g_hash_table_unref (assumed_metrics);
|
||||
|
||||
priv->resync.guard--;
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void
|
||||
_entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx, const Entry *old_entry, gboolean do_sync)
|
||||
_entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx, const Entry *old_entry)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
Entry *entry;
|
||||
|
|
@ -372,24 +592,23 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint
|
|||
entry = g_ptr_array_index (entries, entry_idx);
|
||||
|
||||
g_assert ( !old_entry
|
||||
|| (entry->source.pointer == old_entry->source.pointer && entry->route.ifindex == old_entry->route.ifindex));
|
||||
|| (entry->source.pointer == old_entry->source.pointer && entry->route.rx.ifindex == old_entry->route.rx.ifindex));
|
||||
|
||||
if (!entry->synced) {
|
||||
entry->effective_metric = entry->route.metric;
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": %s %s%s",
|
||||
LOG_ENTRY_ARGS (entry_idx, entry),
|
||||
old_entry ? "update" : "add",
|
||||
vtable->platform_route_to_string (&entry->route),
|
||||
entry->never_default ? " (never-default)" : (entry->synced ? "" : " (not synced)"));
|
||||
}
|
||||
if (!entry->synced && !entry->never_default)
|
||||
entry->effective_metric = entry->route.rx.metric;
|
||||
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": %s %s",
|
||||
LOG_ENTRY_ARGS (entry_idx, entry),
|
||||
old_entry ? "update" : "add",
|
||||
vtable->platform_route_to_string (&entry->route.rx));
|
||||
|
||||
g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL);
|
||||
|
||||
_resync_all (vtable, self, entry, old_entry, do_sync);
|
||||
_resync_all (vtable, self, entry, old_entry, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
_entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx, gboolean do_sync)
|
||||
_entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
Entry *entry;
|
||||
|
|
@ -401,13 +620,14 @@ _entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint
|
|||
|
||||
entry = g_ptr_array_index (entries, entry_idx);
|
||||
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": remove %s (%u%s)", LOG_ENTRY_ARGS (entry_idx, entry), vtable->platform_route_to_string (&entry->route), (guint) entry->effective_metric, entry->synced ? "" : ", not synced");
|
||||
_LOGD (vtable->addr_family, LOG_ENTRY_FMT": remove %s (%u)", LOG_ENTRY_ARGS (entry_idx, entry),
|
||||
vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric);
|
||||
|
||||
/* Remove the entry from the list (but don't free it yet) */
|
||||
g_ptr_array_index (entries, entry_idx) = NULL;
|
||||
g_ptr_array_remove_index (entries, entry_idx);
|
||||
|
||||
_resync_all (vtable, self, NULL, entry, do_sync);
|
||||
_resync_all (vtable, self, NULL, entry, FALSE);
|
||||
|
||||
_entry_free (entry);
|
||||
}
|
||||
|
|
@ -421,17 +641,13 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
|
|||
Entry *entry;
|
||||
guint entry_idx;
|
||||
const NMPlatformIPRoute *default_route = NULL;
|
||||
union {
|
||||
NMPlatformIPRoute vx;
|
||||
NMPlatformIP4Route v4;
|
||||
NMPlatformIP6Route v6;
|
||||
} rt;
|
||||
NMPlatformIPXRoute rt;
|
||||
int ip_ifindex;
|
||||
GPtrArray *entries;
|
||||
NMDevice *device = NULL;
|
||||
NMVpnConnection *vpn = NULL;
|
||||
gboolean never_default = FALSE;
|
||||
gboolean synced;
|
||||
gboolean synced = FALSE;
|
||||
|
||||
g_return_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self));
|
||||
if (NM_IS_DEVICE (source))
|
||||
|
|
@ -460,14 +676,14 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
|
|||
entry = _entry_find_by_source (entries, source, &entry_idx);
|
||||
|
||||
if ( entry
|
||||
&& entry->route.ifindex != ip_ifindex) {
|
||||
&& entry->route.rx.ifindex != ip_ifindex) {
|
||||
/* Strange... the ifindex changed... Remove the device and start again. */
|
||||
_LOGD (vtable->addr_family, "ifindex of "LOG_ENTRY_FMT" changed: %d -> %d",
|
||||
LOG_ENTRY_ARGS (entry_idx, entry),
|
||||
entry->route.ifindex, ip_ifindex);
|
||||
entry->route.rx.ifindex, ip_ifindex);
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
_entry_at_idx_remove (vtable, self, entry_idx, FALSE);
|
||||
_entry_at_idx_remove (vtable, self, entry_idx);
|
||||
g_assert (!_entry_find_by_source (entries, source, NULL));
|
||||
_ipx_update_default_route (vtable, self, source);
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
|
|
@ -477,29 +693,48 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
|
|||
/* get the @default_route from the device. */
|
||||
if (ip_ifindex > 0) {
|
||||
if (device) {
|
||||
gboolean is_assumed;
|
||||
|
||||
if (VTABLE_IS_IP4)
|
||||
default_route = (const NMPlatformIPRoute *) nm_device_get_ip4_default_route (device);
|
||||
default_route = (const NMPlatformIPRoute *) nm_device_get_ip4_default_route (device, &is_assumed);
|
||||
else
|
||||
default_route = (const NMPlatformIPRoute *) nm_device_get_ip6_default_route (device);
|
||||
default_route = (const NMPlatformIPRoute *) nm_device_get_ip6_default_route (device, &is_assumed);
|
||||
if (!default_route && !is_assumed) {
|
||||
/* the device has no default route, but it is not assumed. That means, NMDefaultRouteManager
|
||||
* enforces that the device has no default route.
|
||||
*
|
||||
* Hence we have to keep track of this entry, otherwise a missing entry tells us
|
||||
* that the interface is assumed and NM would not remove the default routes on
|
||||
* the device. */
|
||||
memset (&rt, 0, sizeof (rt));
|
||||
rt.rx.ifindex = ip_ifindex;
|
||||
rt.rx.source = NM_IP_CONFIG_SOURCE_UNKNOWN;
|
||||
rt.rx.metric = G_MAXUINT32;
|
||||
default_route = &rt.rx;
|
||||
|
||||
never_default = TRUE;
|
||||
synced = TRUE;
|
||||
} else
|
||||
synced = default_route && !is_assumed;
|
||||
} else {
|
||||
NMConnection *connection = nm_active_connection_get_connection ((NMActiveConnection *) vpn);
|
||||
|
||||
if ( connection
|
||||
&& nm_vpn_connection_get_vpn_state (vpn) == NM_VPN_CONNECTION_STATE_ACTIVATED) {
|
||||
|
||||
memset (&rt, 0, sizeof (rt));
|
||||
if (VTABLE_IS_IP4) {
|
||||
NMIP4Config *vpn_config;
|
||||
|
||||
vpn_config = nm_vpn_connection_get_ip4_config (vpn);
|
||||
if (vpn_config) {
|
||||
never_default = nm_ip4_config_get_never_default (vpn_config);
|
||||
memset (&rt.v4, 0, sizeof (rt.v4));
|
||||
rt.v4.ifindex = ip_ifindex;
|
||||
rt.v4.source = NM_IP_CONFIG_SOURCE_VPN;
|
||||
rt.v4.gateway = nm_vpn_connection_get_ip4_internal_gateway (vpn);
|
||||
rt.v4.metric = nm_vpn_connection_get_ip4_route_metric (vpn);
|
||||
rt.v4.mss = nm_ip4_config_get_mss (vpn_config);
|
||||
default_route = &rt.vx;
|
||||
rt.r4.ifindex = ip_ifindex;
|
||||
rt.r4.source = NM_IP_CONFIG_SOURCE_VPN;
|
||||
rt.r4.gateway = nm_vpn_connection_get_ip4_internal_gateway (vpn);
|
||||
rt.r4.metric = nm_vpn_connection_get_ip4_route_metric (vpn);
|
||||
rt.r4.mss = nm_ip4_config_get_mss (vpn_config);
|
||||
default_route = &rt.rx;
|
||||
}
|
||||
} else {
|
||||
NMIP6Config *vpn_config;
|
||||
|
|
@ -509,24 +744,20 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
|
|||
const struct in6_addr *int_gw = nm_vpn_connection_get_ip6_internal_gateway (vpn);
|
||||
|
||||
never_default = nm_ip6_config_get_never_default (vpn_config);
|
||||
memset (&rt.v6, 0, sizeof (rt.v6));
|
||||
rt.v6.ifindex = ip_ifindex;
|
||||
rt.v6.source = NM_IP_CONFIG_SOURCE_VPN;
|
||||
rt.v6.gateway = int_gw ? *int_gw : in6addr_any;
|
||||
rt.v6.metric = nm_vpn_connection_get_ip6_route_metric (vpn);
|
||||
rt.v6.mss = nm_ip6_config_get_mss (vpn_config);
|
||||
default_route = &rt.vx;
|
||||
rt.r6.ifindex = ip_ifindex;
|
||||
rt.r6.source = NM_IP_CONFIG_SOURCE_VPN;
|
||||
rt.r6.gateway = int_gw ? *int_gw : in6addr_any;
|
||||
rt.r6.metric = nm_vpn_connection_get_ip6_route_metric (vpn);
|
||||
rt.r6.mss = nm_ip6_config_get_mss (vpn_config);
|
||||
default_route = &rt.rx;
|
||||
}
|
||||
}
|
||||
}
|
||||
synced = TRUE;
|
||||
}
|
||||
}
|
||||
g_assert (!default_route || default_route->plen == 0);
|
||||
|
||||
/* if the source is never_default or the device uses an assumed connection,
|
||||
* we don't sync the route. */
|
||||
synced = !never_default && (!device || !nm_device_uses_assumed_connection (device));
|
||||
|
||||
if (!entry && !default_route)
|
||||
/* nothing to do */;
|
||||
else if (!entry) {
|
||||
|
|
@ -535,31 +766,31 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
|
|||
entry->source.object = g_object_ref (source);
|
||||
|
||||
if (VTABLE_IS_IP4)
|
||||
entry->route4 = *((const NMPlatformIP4Route *) default_route);
|
||||
entry->route.r4 = *((const NMPlatformIP4Route *) default_route);
|
||||
else
|
||||
entry->route6 = *((const NMPlatformIP6Route *) default_route);
|
||||
entry->route.r6 = *((const NMPlatformIP6Route *) default_route);
|
||||
|
||||
/* only use normalized metrics */
|
||||
entry->route.metric = vtable->route_metric_normalize (entry->route.metric);
|
||||
entry->route.ifindex = ip_ifindex;
|
||||
entry->route.rx.metric = vtable->route_metric_normalize (entry->route.rx.metric);
|
||||
entry->route.rx.ifindex = ip_ifindex;
|
||||
entry->never_default = never_default;
|
||||
entry->effective_metric = entry->route.metric;
|
||||
entry->effective_metric = entry->route.rx.metric;
|
||||
entry->synced = synced;
|
||||
|
||||
g_ptr_array_add (entries, entry);
|
||||
_entry_at_idx_update (vtable, self, entries->len - 1, NULL, TRUE);
|
||||
_entry_at_idx_update (vtable, self, entries->len - 1, NULL);
|
||||
} else if (default_route) {
|
||||
/* update */
|
||||
Entry old_entry, new_entry;
|
||||
|
||||
new_entry = *entry;
|
||||
if (VTABLE_IS_IP4)
|
||||
new_entry.route4 = *((const NMPlatformIP4Route *) default_route);
|
||||
new_entry.route.r4 = *((const NMPlatformIP4Route *) default_route);
|
||||
else
|
||||
new_entry.route6 = *((const NMPlatformIP6Route *) default_route);
|
||||
new_entry.route.r6 = *((const NMPlatformIP6Route *) default_route);
|
||||
/* only use normalized metrics */
|
||||
new_entry.route.metric = vtable->route_metric_normalize (new_entry.route.metric);
|
||||
new_entry.route.ifindex = ip_ifindex;
|
||||
new_entry.route.rx.metric = vtable->route_metric_normalize (new_entry.route.rx.metric);
|
||||
new_entry.route.rx.ifindex = ip_ifindex;
|
||||
new_entry.never_default = never_default;
|
||||
new_entry.synced = synced;
|
||||
|
||||
|
|
@ -568,10 +799,10 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
|
|||
|
||||
old_entry = *entry;
|
||||
*entry = new_entry;
|
||||
_entry_at_idx_update (vtable, self, entry_idx, &old_entry, TRUE);
|
||||
_entry_at_idx_update (vtable, self, entry_idx, &old_entry);
|
||||
} else {
|
||||
/* delete */
|
||||
_entry_at_idx_remove (vtable, self, entry_idx, TRUE);
|
||||
_entry_at_idx_remove (vtable, self, entry_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -589,35 +820,6 @@ nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *self,
|
|||
|
||||
/***********************************************************************************/
|
||||
|
||||
static void
|
||||
_ipx_remove_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, gpointer source)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv;
|
||||
guint entry_idx;
|
||||
|
||||
g_return_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self));
|
||||
g_return_if_fail (NM_IS_DEVICE (source) || NM_IS_VPN_CONNECTION (source));
|
||||
|
||||
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
|
||||
if (_entry_find_by_source (vtable->get_entries (priv), source, &entry_idx))
|
||||
_entry_at_idx_remove (vtable, self, entry_idx, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
nm_default_route_manager_ip4_remove_default_route (NMDefaultRouteManager *self, gpointer source)
|
||||
{
|
||||
_ipx_remove_default_route (&vtable_ip4, self, source);
|
||||
}
|
||||
|
||||
void
|
||||
nm_default_route_manager_ip6_remove_default_route (NMDefaultRouteManager *self, gpointer source)
|
||||
{
|
||||
_ipx_remove_default_route (&vtable_ip6, self, source);
|
||||
}
|
||||
|
||||
/***********************************************************************************/
|
||||
|
||||
static gboolean
|
||||
_ipx_connection_has_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, NMConnection *connection)
|
||||
{
|
||||
|
|
@ -686,7 +888,8 @@ _ipx_get_best_device (const VTableIP *vtable, NMDefaultRouteManager *self, const
|
|||
if (!NM_IS_DEVICE (entry->source.pointer))
|
||||
continue;
|
||||
|
||||
g_assert (!entry->never_default);
|
||||
if (entry->never_default)
|
||||
continue;
|
||||
|
||||
if (g_slist_find ((GSList *) devices, entry->source.device))
|
||||
return entry->source.pointer;
|
||||
|
|
@ -840,6 +1043,9 @@ _ipx_get_best_config (const VTableIP *vtable,
|
|||
NMDevice *device = entry->source.device;
|
||||
NMActRequest *req;
|
||||
|
||||
if (entry->never_default)
|
||||
continue;
|
||||
|
||||
if (VTABLE_IS_IP4)
|
||||
config_result = nm_device_get_ip4_config (device);
|
||||
else
|
||||
|
|
@ -931,6 +1137,7 @@ static const VTableIP vtable_ip4 = {
|
|||
.addr_family = AF_INET,
|
||||
.get_entries = _v4_get_entries,
|
||||
.platform_route_to_string = (const char *(*)(const NMPlatformIPRoute *)) nm_platform_ip4_route_to_string,
|
||||
.platform_route_get_all = nm_platform_ip4_route_get_all,
|
||||
.platform_route_delete_default = _v4_platform_route_delete_default,
|
||||
.route_metric_normalize = _v4_route_metric_normalize,
|
||||
};
|
||||
|
|
@ -939,6 +1146,7 @@ static const VTableIP vtable_ip6 = {
|
|||
.addr_family = AF_INET6,
|
||||
.get_entries = _v6_get_entries,
|
||||
.platform_route_to_string = (const char *(*)(const NMPlatformIPRoute *)) nm_platform_ip6_route_to_string,
|
||||
.platform_route_get_all = nm_platform_ip6_route_get_all,
|
||||
.platform_route_delete_default = _v6_platform_route_delete_default,
|
||||
.route_metric_normalize = nm_utils_ip6_route_metric_normalize,
|
||||
};
|
||||
|
|
@ -957,13 +1165,168 @@ nm_default_route_manager_get ()
|
|||
|
||||
/***********************************************************************************/
|
||||
|
||||
static gboolean
|
||||
_resync_idle_now (NMDefaultRouteManager *self)
|
||||
{
|
||||
gboolean has_v4_changes, has_v6_changes;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
|
||||
has_v4_changes = priv->resync.has_v4_changes;
|
||||
has_v6_changes = priv->resync.has_v6_changes;
|
||||
|
||||
_LOGD (0, "resync: sync now (%u) (IPv4 changes: %s, IPv6 changes: %s)", priv->resync.idle_handle,
|
||||
has_v4_changes ? "yes" : "no", has_v6_changes ? "yes" : "no");
|
||||
|
||||
priv->resync.has_v4_changes = FALSE;
|
||||
priv->resync.has_v6_changes = FALSE;
|
||||
priv->resync.idle_handle = 0;
|
||||
priv->resync.backoff_wait_time_ms =
|
||||
priv->resync.backoff_wait_time_ms == 0
|
||||
? 100
|
||||
: priv->resync.backoff_wait_time_ms * 2;
|
||||
|
||||
if (has_v4_changes)
|
||||
changed |= _resync_all (&vtable_ip4, self, NULL, NULL, TRUE);
|
||||
|
||||
if (has_v6_changes)
|
||||
changed |= _resync_all (&vtable_ip6, self, NULL, NULL, TRUE);
|
||||
|
||||
if (!changed) {
|
||||
/* Nothing changed: reset the backoff wait time */
|
||||
_resync_idle_cancel (self);
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
_resync_idle_cancel (NMDefaultRouteManager *self)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
|
||||
if (priv->resync.idle_handle) {
|
||||
_LOGD (0, "resync: cancelled (%u)", priv->resync.idle_handle);
|
||||
g_source_remove (priv->resync.idle_handle);
|
||||
priv->resync.idle_handle = 0;
|
||||
}
|
||||
priv->resync.backoff_wait_time_ms = 0;
|
||||
priv->resync.has_v4_changes = FALSE;
|
||||
priv->resync.has_v6_changes = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
_resync_idle_reschedule (NMDefaultRouteManager *self)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
|
||||
/* since we react on external changes and readd/remove default routes for
|
||||
* the interfaces we manage, there could be the erronous situation where two applications
|
||||
* fight over a certain default route.
|
||||
* Avoid this, by increasingly wait longer to touch the system (backoff wait time). */
|
||||
|
||||
if (priv->resync.backoff_wait_time_ms == 0) {
|
||||
/* for scheduling idle, always reschedule (to process all other events first) */
|
||||
if (priv->resync.idle_handle)
|
||||
g_source_remove (priv->resync.idle_handle);
|
||||
else
|
||||
_LOGD (0, "resync: schedule on idle");
|
||||
priv->resync.idle_handle = g_idle_add ((GSourceFunc) _resync_idle_now, self);
|
||||
} else if (!priv->resync.idle_handle) {
|
||||
priv->resync.idle_handle = g_timeout_add (priv->resync.backoff_wait_time_ms, (GSourceFunc) _resync_idle_now, self);
|
||||
_LOGD (0, "resync: schedule in %u.%03u seconds (%u)", priv->resync.backoff_wait_time_ms/1000,
|
||||
priv->resync.backoff_wait_time_ms%1000, priv->resync.idle_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_platform_ipx_route_changed_cb (const VTableIP *vtable,
|
||||
NMDefaultRouteManager *self,
|
||||
const NMPlatformIPRoute *route)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv;
|
||||
|
||||
if (route && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) {
|
||||
/* we only care about address changes or changes of default route. */
|
||||
return;
|
||||
}
|
||||
|
||||
priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
|
||||
if (priv->resync.guard) {
|
||||
/* callbacks while executing _resync_all() are ignored. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (VTABLE_IS_IP4)
|
||||
priv->resync.has_v4_changes = TRUE;
|
||||
else
|
||||
priv->resync.has_v6_changes = TRUE;
|
||||
|
||||
_resync_idle_reschedule (self);
|
||||
}
|
||||
|
||||
static void
|
||||
_platform_ip4_address_changed_cb (NMPlatform *platform,
|
||||
int ifindex,
|
||||
gpointer platform_object,
|
||||
NMPlatformSignalChangeType change_type,
|
||||
NMPlatformReason reason,
|
||||
NMDefaultRouteManager *self)
|
||||
{
|
||||
_platform_ipx_route_changed_cb (&vtable_ip4, self, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
_platform_ip6_address_changed_cb (NMPlatform *platform,
|
||||
int ifindex,
|
||||
gpointer platform_object,
|
||||
NMPlatformSignalChangeType change_type,
|
||||
NMPlatformReason reason,
|
||||
NMDefaultRouteManager *self)
|
||||
{
|
||||
_platform_ipx_route_changed_cb (&vtable_ip6, self, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
_platform_ip4_route_changed_cb (NMPlatform *platform,
|
||||
int ifindex,
|
||||
gpointer platform_object,
|
||||
NMPlatformSignalChangeType change_type,
|
||||
NMPlatformReason reason,
|
||||
NMDefaultRouteManager *self)
|
||||
{
|
||||
_platform_ipx_route_changed_cb (&vtable_ip4, self, platform_object);
|
||||
}
|
||||
|
||||
static void
|
||||
_platform_ip6_route_changed_cb (NMPlatform *platform,
|
||||
int ifindex,
|
||||
gpointer platform_object,
|
||||
NMPlatformSignalChangeType change_type,
|
||||
NMPlatformReason reason,
|
||||
NMDefaultRouteManager *self)
|
||||
{
|
||||
_platform_ipx_route_changed_cb (&vtable_ip6, self, platform_object);
|
||||
}
|
||||
|
||||
/***********************************************************************************/
|
||||
|
||||
static void
|
||||
nm_default_route_manager_init (NMDefaultRouteManager *self)
|
||||
{
|
||||
NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
|
||||
NMPlatform *platform;
|
||||
|
||||
priv->entries_ip4 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free);
|
||||
priv->entries_ip6 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free);
|
||||
|
||||
platform = nm_platform_get ();
|
||||
g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_platform_ip4_address_changed_cb), self);
|
||||
g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_platform_ip6_address_changed_cb), self);
|
||||
g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_platform_ip4_route_changed_cb), self);
|
||||
g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_platform_ip6_route_changed_cb), self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -981,6 +1344,10 @@ dispose (GObject *object)
|
|||
priv->entries_ip6 = NULL;
|
||||
}
|
||||
|
||||
_resync_idle_cancel (self);
|
||||
|
||||
g_signal_handlers_disconnect_by_data (nm_platform_get (), self);
|
||||
|
||||
G_OBJECT_CLASS (nm_default_route_manager_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,9 +51,6 @@ NMDefaultRouteManager *nm_default_route_manager_get (void);
|
|||
void nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *manager, gpointer source);
|
||||
void nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *manager, gpointer source);
|
||||
|
||||
void nm_default_route_manager_ip4_remove_default_route (NMDefaultRouteManager *manager, gpointer source);
|
||||
void nm_default_route_manager_ip6_remove_default_route (NMDefaultRouteManager *manager, gpointer source);
|
||||
|
||||
gboolean nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection);
|
||||
gboolean nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection);
|
||||
|
||||
|
|
|
|||
|
|
@ -175,6 +175,12 @@ struct _NMPlatformIP6Address {
|
|||
};
|
||||
G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Address, address));
|
||||
|
||||
typedef union {
|
||||
NMPlatformIPAddress ax;
|
||||
NMPlatformIP4Address a4;
|
||||
NMPlatformIP6Address a6;
|
||||
} NMPlatformIPXAddress;
|
||||
|
||||
#undef __NMPlatformIPAddress_COMMON
|
||||
|
||||
|
||||
|
|
@ -215,6 +221,12 @@ struct _NMPlatformIP6Route {
|
|||
};
|
||||
G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Route, network));
|
||||
|
||||
typedef union {
|
||||
NMPlatformIPRoute rx;
|
||||
NMPlatformIP4Route r4;
|
||||
NMPlatformIP6Route r6;
|
||||
} NMPlatformIPXRoute;
|
||||
|
||||
#undef __NMPlatformIPRoute_COMMON
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -237,9 +237,6 @@ vpn_cleanup (NMVpnConnection *connection, NMDevice *parent_dev)
|
|||
nm_platform_address_flush (priv->ip_ifindex);
|
||||
}
|
||||
|
||||
nm_default_route_manager_ip4_remove_default_route (nm_default_route_manager_get (), connection);
|
||||
nm_default_route_manager_ip6_remove_default_route (nm_default_route_manager_get (), connection);
|
||||
|
||||
nm_device_set_vpn4_config (parent_dev, NULL);
|
||||
nm_device_set_vpn6_config (parent_dev, NULL);
|
||||
|
||||
|
|
@ -327,10 +324,8 @@ _set_vpn_state (NMVpnConnection *connection,
|
|||
|
||||
dispatcher_cleanup (connection);
|
||||
|
||||
if (vpn_state >= STATE_DISCONNECTED && vpn_state <= STATE_FAILED) {
|
||||
nm_default_route_manager_ip4_remove_default_route (nm_default_route_manager_get (), connection);
|
||||
nm_default_route_manager_ip6_remove_default_route (nm_default_route_manager_get (), connection);
|
||||
}
|
||||
nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), connection);
|
||||
nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), connection);
|
||||
|
||||
/* The connection gets destroyed by the VPN manager when it enters the
|
||||
* disconnected/failed state, but we need to keep it around for a bit
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue