From 416e30c642fe96a604079e66fc8c52f87a508d31 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Sat, 2 Nov 2024 16:21:53 -0500 Subject: [PATCH] networkmanager: add support for "routing-policy" NetworkManager supports ip rules via the "routing-ruleX" field. Let's teach the networkmanager backend to emit "routing-ruleX" entries for each "routing-policy" entry in the netplan yaml. Update the tests for the new behavior. --- src/nm.c | 55 ++++++++++++++++++++++++++++++++++++ tests/generator/test_vrfs.py | 1 + tests/integration/routing.py | 6 ++-- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/nm.c b/src/nm.c index 3dbb85bfc..0d6934b75 100644 --- a/src/nm.c +++ b/src/nm.c @@ -254,6 +254,54 @@ write_routes_nm(const NetplanNetDefinition* def, GKeyFile *kf, gint family, GErr j++; } } + + return TRUE; +} + +STATIC gboolean +write_ip_rules_nm(const NetplanNetDefinition* def, GKeyFile *kf, gint family, __unused GError** error) +{ + const gchar* group = NULL; + gchar* tmp_key = NULL; + GString* tmp_val = NULL; + + if (family == AF_INET) + group = "ipv4"; + else if (family == AF_INET6) + group = "ipv6"; + g_assert(group != NULL); + + if (def->ip_rules != NULL) { + for (unsigned i = 0, j = 1; i < def->ip_rules->len; ++i) { + const NetplanIPRule *cur_rule = g_array_index(def->ip_rules, NetplanIPRule*, i); + + if (cur_rule->family != family) + continue; + + tmp_key = g_strdup_printf("routing-rule%d", j); + tmp_val = g_string_new(NULL); + + if (cur_rule->from) + g_string_append_printf(tmp_val, "from %s ", cur_rule->from); + if (cur_rule->to) + g_string_append_printf(tmp_val, "to %s ", cur_rule->to); + if (cur_rule->tos != NETPLAN_IP_RULE_TOS_UNSPEC) + g_string_append_printf(tmp_val, "tos %u ", cur_rule->tos); + if (cur_rule->fwmark != NETPLAN_IP_RULE_FW_MARK_UNSPEC) + g_string_append_printf(tmp_val, "fwmark %u ", cur_rule->fwmark); + if (cur_rule->table != NETPLAN_ROUTE_TABLE_UNSPEC) + g_string_append_printf(tmp_val, "table %u ", cur_rule->table); + if (cur_rule->priority != NETPLAN_IP_RULE_PRIO_UNSPEC) + g_string_append_printf(tmp_val, "priority %u ", cur_rule->priority); + g_string_truncate(tmp_val, tmp_val->len - 1); //remove trailing space + g_key_file_set_string(kf, group, tmp_key, tmp_val->str); + g_free(tmp_key); + g_string_free(tmp_val, TRUE); + + j++; + } + } + return TRUE; } @@ -716,6 +764,8 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir, g_key_file_set_uint64(kf, "vrf", "table", def->vrf_table); if (!write_routes_nm(def, kf, AF_INET, error) || !write_routes_nm(def, kf, AF_INET6, error)) return FALSE; + if (!write_ip_rules_nm(def, kf, AF_INET, error) || !write_ip_rules_nm(def, kf, AF_INET6, error)) + return FALSE; } if (def->type == NETPLAN_DEF_TYPE_VETH && def->veth_peer_link) { @@ -871,6 +921,8 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir, write_search_domains(def, "ipv4", kf); if (!write_routes_nm(def, kf, AF_INET, error)) return FALSE; + if (!write_ip_rules_nm(def, kf, AF_INET, error)) + return FALSE; } if (!def->dhcp4_overrides.use_routes) { @@ -917,6 +969,9 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir, if (!write_routes_nm(def, kf, AF_INET6, error)) return FALSE; + if (!write_ip_rules_nm(def, kf, AF_INET6, error)) + return FALSE; + if (!def->dhcp6_overrides.use_routes) { g_key_file_set_boolean(kf, "ipv6", "ignore-auto-routes", TRUE); g_key_file_set_boolean(kf, "ipv6", "never-default", TRUE); diff --git a/tests/generator/test_vrfs.py b/tests/generator/test_vrfs.py index 23d0966e6..cf3520d2a 100644 --- a/tests/generator/test_vrfs.py +++ b/tests/generator/test_vrfs.py @@ -66,6 +66,7 @@ def test_vrf_set_table(self): [ipv4] route1=0.0.0.0/0,1.2.3.4 route1_options=table=1005 +routing-rule1=from 2.3.4.5 table 1005 method=link-local [ipv6] diff --git a/tests/integration/routing.py b/tests/integration/routing.py index 6afd75c6d..60fa96153 100644 --- a/tests/integration/routing.py +++ b/tests/integration/routing.py @@ -332,10 +332,8 @@ def test_vrf_basic(self): self.assertIn('11.11.11.0/24 via 10.10.10.2 dev {}'.format(self.dev_e_client), out) # verify routing policy was setup correctly to the VRF's table - # 'routing-policy' is not supported on NetworkManager - if self.backend == 'networkd': - out = subprocess.check_output(['ip', 'rule', 'show'], text=True) - self.assertIn('from 10.10.10.42 lookup 1000', out) + out = subprocess.check_output(['ip', 'rule', 'show'], text=True) + self.assertIn('from 10.10.10.42 lookup 1000', out) def test_route_advmss_v6(self): self.setup_eth(None)