core: add handling of IP routing rules to NMDevice

This commit is contained in:
Thomas Haller
2019-03-23 15:09:29 +01:00
parent ba59c7c3c0
commit 3f9347745b
6 changed files with 152 additions and 7 deletions

View File

@@ -23,6 +23,8 @@
#include "NetworkManagerUtils.h"
#include <linux/fib_rules.h>
#include "nm-utils/nm-c-list.h"
#include "nm-common-macros.h"
@@ -908,6 +910,48 @@ nm_match_spec_device_by_pllink (const NMPlatformLink *pllink,
/*****************************************************************************/
NMPlatformRoutingRule *
nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule,
NMPlatformRoutingRule *out_pl)
{
nm_assert (rule);
nm_assert (nm_ip_routing_rule_validate (rule, NULL));
nm_assert (out_pl);
*out_pl = (NMPlatformRoutingRule) {
.addr_family = nm_ip_routing_rule_get_addr_family (rule),
.flags = ( nm_ip_routing_rule_get_invert (rule)
? FIB_RULE_INVERT
: 0),
.priority = nm_ip_routing_rule_get_priority (rule),
.tos = nm_ip_routing_rule_get_tos (rule),
.ip_proto = nm_ip_routing_rule_get_ipproto (rule),
.fwmark = nm_ip_routing_rule_get_fwmark (rule),
.fwmask = nm_ip_routing_rule_get_fwmask (rule),
.sport_range = {
.start = nm_ip_routing_rule_get_source_port_start (rule),
.end = nm_ip_routing_rule_get_source_port_end (rule),
},
.dport_range = {
.start = nm_ip_routing_rule_get_destination_port_start (rule),
.end = nm_ip_routing_rule_get_destination_port_end (rule),
},
.src = *(nm_ip_routing_rule_get_from_bin (rule) ?: &nm_ip_addr_zero),
.dst = *(nm_ip_routing_rule_get_to_bin (rule) ?: &nm_ip_addr_zero),
.src_len = nm_ip_routing_rule_get_from_len (rule),
.dst_len = nm_ip_routing_rule_get_to_len (rule),
.action = nm_ip_routing_rule_get_action (rule),
.table = nm_ip_routing_rule_get_table (rule),
};
nm_ip_routing_rule_get_xifname_bin (rule, TRUE, out_pl->iifname);
nm_ip_routing_rule_get_xifname_bin (rule, FALSE, out_pl->oifname);
return out_pl;
}
/*****************************************************************************/
struct _NMShutdownWaitObjHandle {
CList lst;
GObject *watched_obj;

View File

@@ -24,6 +24,9 @@
#include "nm-core-utils.h"
#include "nm-setting-ip-config.h"
#include "platform/nm-platform.h"
/*****************************************************************************/
const char *nm_utils_get_ip_config_method (NMConnection *connection,
@@ -60,6 +63,11 @@ int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink,
/*****************************************************************************/
NMPlatformRoutingRule *nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule,
NMPlatformRoutingRule *out_pl);
/*****************************************************************************/
/* during shutdown, there are two relevant timeouts. One is
* NM_SHUTDOWN_TIMEOUT_MS which is plenty of time, that we give for all
* actions to complete. Of course, during shutdown components should hurry

View File

@@ -47,6 +47,7 @@
#include "nm-manager.h"
#include "platform/nm-platform.h"
#include "platform/nmp-object.h"
#include "platform/nmp-rules-manager.h"
#include "ndisc/nm-ndisc.h"
#include "ndisc/nm-lndp-ndisc.h"
#include "dhcp/nm-dhcp-manager.h"
@@ -6408,6 +6409,84 @@ lldp_init (NMDevice *self, gboolean restart)
}
}
/* set-mode can be:
* - TRUE: sync with new rules.
* - FALSE: sync, but remove all rules (== flush)
* - DEFAULT: forget about all the rules that we previously tracked,
* but don't actually remove them. This is when quitting NM
* we want to keep the rules.
* The problem is, after restart of NM, the rule manager will
* no longer remember that NM added these rules and treat them
* as externally added ones. Don't restart NetworkManager if
* you care about that.
*/
static void
_routing_rules_sync (NMDevice *self,
NMTernary set_mode)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMPRulesManager *rules_manager = nm_netns_get_rules_manager (nm_device_get_netns (self));
gboolean untrack_only_dirty = FALSE;
gboolean keep_deleted_rules;
gpointer user_tag;
user_tag = priv;
if (set_mode == NM_TERNARY_TRUE) {
NMConnection *applied_connection;
NMSettingIPConfig *s_ip;
guint i, num;
int is_ipv4;
untrack_only_dirty = TRUE;
nmp_rules_manager_set_dirty (rules_manager, user_tag);
applied_connection = nm_device_get_applied_connection (self);
for (is_ipv4 = 0; applied_connection && is_ipv4 < 2; is_ipv4++) {
int addr_family = is_ipv4 ? AF_INET : AF_INET6;
s_ip = nm_connection_get_setting_ip_config (applied_connection, addr_family);
if (!s_ip)
continue;
num = nm_setting_ip_config_get_num_routing_rules (s_ip);
for (i = 0; i < num; i++) {
NMPlatformRoutingRule plrule;
NMIPRoutingRule *rule;
rule = nm_setting_ip_config_get_routing_rule (s_ip, i);
nm_ip_routing_rule_to_platform (rule, &plrule);
nmp_rules_manager_track (rules_manager,
&plrule,
10,
user_tag);
}
}
}
nmp_rules_manager_untrack_all (rules_manager, user_tag, !untrack_only_dirty);
keep_deleted_rules = FALSE;
if (set_mode == NM_TERNARY_DEFAULT) {
/* when exiting NM, we leave the device up and the rules configured.
* We just all nmp_rules_manager_sync() to forget about the synced rules,
* but we don't actually delete them.
*
* FIXME: that is a problem after restart of NetworkManager, because these
* rules will look like externally added, and NM will no longer remove
* them.
* To fix that, we could during "assume" mark the rules of the profile
* as owned (and "added" by the device). The problem with that is that it
* wouldn't cover rules that devices add by internal decision (not because
* of a setting in the profile, e.g. WireGuard could setup policy routing).
* Maybe it would be better to remember these orphaned rules at exit in a
* file and track them after restart again. */
keep_deleted_rules = TRUE;
}
nmp_rules_manager_sync (rules_manager, keep_deleted_rules);
}
static gboolean
tc_commit (NMDevice *self)
{
@@ -6519,6 +6598,8 @@ activate_stage2_device_config (NMDevice *self)
}
}
_routing_rules_sync (self, NM_TERNARY_TRUE);
if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
if (!nm_device_bring_up (self, FALSE, &no_firmware)) {
if (no_firmware)
@@ -14350,6 +14431,11 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
}
}
_routing_rules_sync (self,
cleanup_type == CLEANUP_TYPE_KEEP
? NM_TERNARY_DEFAULT
: NM_TERNARY_FALSE);
if (ifindex > 0)
nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), ifindex, NULL);

View File

@@ -471,7 +471,8 @@ nmp_rules_manager_untrack_all (NMPRulesManager *self,
}
void
nmp_rules_manager_sync (NMPRulesManager *self)
nmp_rules_manager_sync (NMPRulesManager *self,
gboolean keep_deleted_rules)
{
const NMDedupMultiHeadEntry *pl_head_entry;
NMDedupMultiIter pl_iter;
@@ -486,7 +487,7 @@ nmp_rules_manager_sync (NMPRulesManager *self)
if (!self->by_data)
return;
_LOGD ("sync");
_LOGD ("sync%s", keep_deleted_rules ? " (don't remove any rules)" : "");
pl_head_entry = nm_platform_lookup_obj_type (self->platform, NMP_OBJECT_TYPE_ROUTING_RULE);
if (pl_head_entry) {
@@ -508,6 +509,11 @@ nmp_rules_manager_sync (NMPRulesManager *self)
obj_data->added_by_us = FALSE;
}
if (keep_deleted_rules) {
_LOGD ("forget/leak rule added by us: %s", nmp_object_to_string (plobj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
continue;
}
if (!rules_to_delete)
rules_to_delete = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref);

View File

@@ -54,7 +54,8 @@ void nmp_rules_manager_untrack_all (NMPRulesManager *self,
gconstpointer user_tag,
gboolean all /* or only dirty */);
void nmp_rules_manager_sync (NMPRulesManager *self);
void nmp_rules_manager_sync (NMPRulesManager *self,
gboolean keep_deleted_rules);
/*****************************************************************************/

View File

@@ -1546,12 +1546,12 @@ again:
USER_TAG_2);
}
if (nmtst_get_rand_int () % objs_sync->len == 0) {
nmp_rules_manager_sync (rules_manager);
nmp_rules_manager_sync (rules_manager, FALSE);
g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1);
}
}
nmp_rules_manager_sync (rules_manager);
nmp_rules_manager_sync (rules_manager, FALSE);
g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len);
for (i = 0; i < objs_sync->len; i++) {
@@ -1578,12 +1578,12 @@ again:
break;
}
if (nmtst_get_rand_int () % objs_sync->len == 0) {
nmp_rules_manager_sync (rules_manager);
nmp_rules_manager_sync (rules_manager, FALSE);
g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len - i - 1);
}
}
nmp_rules_manager_sync (rules_manager);
nmp_rules_manager_sync (rules_manager, FALSE);
} else {
for (i = 0; i < objs->len;) {