NetworkManager/src/nm-manager.h
Thomas Haller bd2d71754b device: generate unique default route-metrics per interface
In the past we had NMDefaultRouteManager which would coordinate adding
the default-route with identical metrics. That especially happened, when
activating two devices of the same type, without explicitly specifying
ipv4.route-metric. For example, with ethernet devices, the routes on
both interfaces would get a metric of 100.

Coordinating routes was especially necessary, because we added
routes with NLM_F_EXCL flag, akin to `ip route replace`. We not
only had to avoid that activating two devices in NetworkManager would
result in a fight over the default-route, but more importently
to preserve externally added default-routes on unmanaged interfaces.

NMDefaultRouteManager would ensure that in case of duplicate
metrics, that the device that activated first would keep the
best default-route. It would do so by bumping the metric
of the second device to find a unused metric. The bumping itself
was not very important -- MDefaultRouteManager could also just not
configure any default-routes that show up as second, the result
would be quite similar. More important was to keep the best
default-route on the first activating device until the device
deactivates or a device activates that really has a better
default-route..

Likewise, NMRouteManager would globally manage non-default-routes.
It would not do any bumping of metrics, but it would also ensure that the routes
of the device that activates first are not overwritten by a device activating
later.

However, the `ip route replace` approach has downsides, especially
that it messes with routes on other interfaces, interfaces that are
possibly not managed by NetworkManager. Another downside is, that
binding a socket to an interface might not result in correct
routes, because the route might just not be there (in case of
NMRouteManager, which wouldn't configure duplicate routes by bumping
their metric).

Since commit 77ec302714 we would no longer
use NLM_F_EXCL, but add routes akin to `ip route append`. When
activating for example two ethernet devices with no explict route
metric configuration, there are two routes like

   default via 10.16.122.254 dev eth0 proto dhcp metric 100
   default via 192.168.100.1 dev eth1 proto dhcp metric 100

This does not only affect default routes. In case of a multi-homing
setup you'd get

  192.168.100.0/24 dev eth0 proto kernel scope link src 192.168.100.1 metric 100
  192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.1 metric 100

but it's visible the most for default-routes.

Note that we would append the routes that are activated later, as the order
of `ip route show` confirms. One might hence expect, that kernel selects
a route based on the order in the routing tables. However, that isn't
the case, and activating the second interface will non-deterministically
re-route traffic via the new interface. That will interfere badly with
with NAT, stateful firewalls, and existing connections (like TCP).

The solution is to have NMManager keep a global index of the default route-metrics
currently in use. So, instead of determining the default-route metric based solely
on the device-type, we now in addition generate default metrics that do not
overlap. For example, if you activate eth0 first, it gets route-metric 100,
and if you then activate eth1, it gets 101. Note that if you deactivate
and re-activate eth0, then it will get route-metric 102, because the
best route should stick on eth1 (which reserves the range 100 to 101).

Note that when a connection explititly selects a particular metric, then that
choice is honored (contrary to NMDefaultRouteManager which was more concerned
with avoiding conflicts, then keeping the exact metric).

https://bugzilla.redhat.com/show_bug.cgi?id=1505893
(cherry picked from commit 6a32c64d8f)
2017-12-15 11:44:52 +01:00

158 lines
7.8 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2007 - 2008 Novell, Inc.
* Copyright (C) 2007 - 2010 Red Hat, Inc.
*/
#ifndef __NETWORKMANAGER_MANAGER_H__
#define __NETWORKMANAGER_MANAGER_H__
#include "nm-exported-object.h"
#include "settings/nm-settings-connection.h"
#include "nm-utils/c-list.h"
#define NM_TYPE_MANAGER (nm_manager_get_type ())
#define NM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MANAGER, NMManager))
#define NM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MANAGER, NMManagerClass))
#define NM_IS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MANAGER))
#define NM_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MANAGER))
#define NM_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MANAGER, NMManagerClass))
#define NM_MANAGER_VERSION "version"
#define NM_MANAGER_CAPABILITIES "capabilities"
#define NM_MANAGER_STATE "state"
#define NM_MANAGER_STARTUP "startup"
#define NM_MANAGER_NETWORKING_ENABLED "networking-enabled"
#define NM_MANAGER_WIRELESS_ENABLED "wireless-enabled"
#define NM_MANAGER_WIRELESS_HARDWARE_ENABLED "wireless-hardware-enabled"
#define NM_MANAGER_WWAN_ENABLED "wwan-enabled"
#define NM_MANAGER_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled"
#define NM_MANAGER_WIMAX_ENABLED "wimax-enabled"
#define NM_MANAGER_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled"
#define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections"
#define NM_MANAGER_CONNECTIVITY "connectivity"
#define NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE "connectivity-check-available"
#define NM_MANAGER_CONNECTIVITY_CHECK_ENABLED "connectivity-check-enabled"
#define NM_MANAGER_PRIMARY_CONNECTION "primary-connection"
#define NM_MANAGER_PRIMARY_CONNECTION_TYPE "primary-connection-type"
#define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection"
#define NM_MANAGER_DEVICES "devices"
#define NM_MANAGER_METERED "metered"
#define NM_MANAGER_GLOBAL_DNS_CONFIGURATION "global-dns-configuration"
#define NM_MANAGER_ALL_DEVICES "all-devices"
/* Not exported */
#define NM_MANAGER_SLEEPING "sleeping"
/* signals */
#define NM_MANAGER_CHECK_PERMISSIONS "check-permissions"
#define NM_MANAGER_DEVICE_ADDED "device-added"
#define NM_MANAGER_DEVICE_REMOVED "device-removed"
#define NM_MANAGER_STATE_CHANGED "state-changed"
#define NM_MANAGER_USER_PERMISSIONS_CHANGED "user-permissions-changed"
/* Internal signals */
#define NM_MANAGER_ACTIVE_CONNECTION_ADDED "active-connection-added"
#define NM_MANAGER_ACTIVE_CONNECTION_REMOVED "active-connection-removed"
#define NM_MANAGER_CONFIGURE_QUIT "configure-quit"
#define NM_MANAGER_INTERNAL_DEVICE_ADDED "internal-device-added"
#define NM_MANAGER_INTERNAL_DEVICE_REMOVED "internal-device-removed"
GType nm_manager_get_type (void);
/* nm_manager_setup() should only be used by main.c */
NMManager * nm_manager_setup (void);
NMManager * nm_manager_get (void);
gboolean nm_manager_start (NMManager *manager,
GError **error);
void nm_manager_stop (NMManager *manager);
NMState nm_manager_get_state (NMManager *manager);
const CList * nm_manager_get_active_connections (NMManager *manager);
#define nm_manager_for_each_active_connection(manager, iter, tmp_list) \
for (tmp_list = nm_manager_get_active_connections (manager), \
iter = c_list_entry (tmp_list->next, NMActiveConnection, active_connections_lst); \
({ \
gboolean _has_next = (&iter->active_connections_lst != tmp_list); \
\
if (!_has_next) \
iter = NULL; \
_has_next; \
}); \
iter = c_list_entry (iter->active_connections_lst.next, NMActiveConnection, active_connections_lst))
NMSettingsConnection **nm_manager_get_activatable_connections (NMManager *manager,
guint *out_len,
gboolean sort);
void nm_manager_write_device_state (NMManager *manager);
/* Device handling */
const GSList * nm_manager_get_devices (NMManager *manager);
NMDevice * nm_manager_get_device_by_ifindex (NMManager *manager,
int ifindex);
NMDevice * nm_manager_get_device_by_path (NMManager *manager,
const char *path);
guint32 nm_manager_device_route_metric_reserve (NMManager *self,
int ifindex,
NMDeviceType device_type);
guint32 nm_manager_device_route_metric_get (NMManager *self,
int ifindex);
void nm_manager_device_route_metric_clear (NMManager *self,
int ifindex);
char * nm_manager_get_connection_iface (NMManager *self,
NMConnection *connection,
NMDevice **out_parent,
GError **error);
const char * nm_manager_iface_for_uuid (NMManager *self,
const char *uuid);
NMActiveConnection *nm_manager_activate_connection (NMManager *manager,
NMSettingsConnection *connection,
NMConnection *applied_connection,
const char *specific_object,
NMDevice *device,
NMAuthSubject *subject,
NMActivationType activation_type,
GError **error);
gboolean nm_manager_deactivate_connection (NMManager *manager,
NMActiveConnection *active,
NMDeviceStateReason reason,
GError **error);
void nm_manager_set_capability (NMManager *self, NMCapability cap);
NMDevice * nm_manager_get_device (NMManager *self,
const char *ifname,
NMDeviceType device_type);
gboolean nm_manager_remove_device (NMManager *self,
const char *ifname,
NMDeviceType device_type);
#endif /* __NETWORKMANAGER_MANAGER_H__ */