From cfbd14cd8ab4df0525c9c2aecb47b8b2fe3f01a2 Mon Sep 17 00:00:00 2001 From: Wen Liang Date: Thu, 22 Sep 2022 13:38:44 -0400 Subject: [PATCH] Support looking up named route table in routing rule The user may need to define the named route table in the routing rule besides the table id, add support for that. The commit fixes https://github.com/linux-system-roles/network/issues/506. Signed-off-by: Wen Liang --- README.md | 5 ++- .../network_lsr/argument_validator.py | 5 ++- tests/playbooks/tests_routing_rules.yml | 36 ++++++++++++++++++ tests/unit/test_network_connections.py | 37 +++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3abebfe..8bb9817 100644 --- a/README.md +++ b/README.md @@ -581,7 +581,10 @@ The IP configuration supports the following options: - `suppress_prefixlength` - Reject routing decisions that have a prefix length of the specified or less. - `table` - - The route table to look up for the `to-table` action. + The route table to look up for the `to-table` action. `table` supports both the + numeric table and named table. In order to specify the named table, the users + have to ensure the named table is properly defined in `/etc/iproute2/rt_tables` + or `/etc/iproute2/rt_tables.d/*.conf`. - `to` - The destination address of the packet to match (e.g. `192.168.100.58/24`). - `tos` - diff --git a/module_utils/network_lsr/argument_validator.py b/module_utils/network_lsr/argument_validator.py index 6f43c93..e297168 100644 --- a/module_utils/network_lsr/argument_validator.py +++ b/module_utils/network_lsr/argument_validator.py @@ -2336,7 +2336,10 @@ class ArgValidator_ListConnections(ArgValidatorList): VALIDATE_ONE_MODE_INITSCRIPTS = "initscripts" def validate_route_tables(self, connection, idx): - for r in connection["ip"]["route"]: + rule_route_combined_list = ( + connection["ip"]["route"] + connection["ip"]["routing_rule"] + ) + for r in rule_route_combined_list: if isinstance(r["table"], Util.STRING_TYPE): mapping = IPRouteUtils.get_route_tables_mapping() if r["table"] in mapping: diff --git a/tests/playbooks/tests_routing_rules.yml b/tests/playbooks/tests_routing_rules.yml index a659a6a..0038cc7 100644 --- a/tests/playbooks/tests_routing_rules.yml +++ b/tests/playbooks/tests_routing_rules.yml @@ -19,6 +19,14 @@ state: present - include_tasks: tasks/assert_device_present.yml + - name: Create a dedicated test file in `/etc/iproute2/rt_tables.d/` and + add a new routing table + lineinfile: + path: /etc/iproute2/rt_tables.d/table.conf + line: "200 custom" + mode: "0644" + create: yes + - name: Configure connection profile and specify the numeric table in static routes import_role: @@ -91,6 +99,9 @@ dport: 128 - 256 invert: True table: 30600 + - priority: 200 + from: 198.51.100.56/26 + table: custom # the routing rule selector sport and ipproto are not supported by iproute # since v4.17.0, and the iproute installed in CentOS-7 and RHEL-7 is # v4.11.0 @@ -115,6 +126,13 @@ changed_when: false when: ansible_distribution_major_version != "7" + - name: Get the routing rule for looking up the table 'custom' + command: ip rule list table custom + register: route_rule_table_custom + ignore_errors: yes + changed_when: false + when: ansible_distribution_major_version != "7" + - name: Get the IPv4 routing rule for the connection "{{ interface }}" command: nmcli -f ipv4.routing-rules c show "{{ interface }}" register: connection_route_rule @@ -171,6 +189,16 @@ specified rule" when: ansible_distribution_major_version != "7" + - name: Assert that the routing rule with 'custom' table lookup matches the + specified rule + assert: + that: + - route_rule_table_custom.stdout is search("200:(\s+)from + 198.51.100.56/26 lookup custom") + msg: "the routing rule with 'custom' table lookup does not match the + specified rule" + when: ansible_distribution_major_version != "7" + - name: Assert that the IPv4 routing rule in the connection "{{ interface }}" matches the specified rule assert: @@ -191,6 +219,8 @@ 0.0.0.0/0 iif iiftest table 30400") - connection_route_rule.stdout is search("priority 30402 from 0.0.0.0/0 oif oiftest table 30400") + - connection_route_rule.stdout is search("priority 200 from + 198.51.100.56/26 table 200") msg: "the IPv4 routing rule in the connection '{{ interface }}' does not match the specified rule" @@ -206,6 +236,12 @@ ::/0 dport 128-256 table 30600") msg: "the IPv6 routing rule in the connection '{{ interface }}' does not match the specified rule" + + - name: Remove the dedicated test file in `/etc/iproute2/rt_tables.d/` + file: + state: absent + path: /etc/iproute2/rt_tables.d/table.conf + - import_playbook: down_profile+delete_interface.yml vars: profile: "{{ interface }}" diff --git a/tests/unit/test_network_connections.py b/tests/unit/test_network_connections.py index 3546de4..196a636 100644 --- a/tests/unit/test_network_connections.py +++ b/tests/unit/test_network_connections.py @@ -4395,6 +4395,7 @@ class TestValidatorRouteTable(Python26CompatTestCase): "table": 30400, }, ], + "routing_rule": [], }, } ] @@ -4650,6 +4651,25 @@ class TestValidatorRoutingRules(Python26CompatTestCase): } ] self.validator = network_lsr.argument_validator.ArgValidator_ListConnections() + self.old_getter = ( + network_lsr.argument_validator.IPRouteUtils.get_route_tables_mapping + ) + network_lsr.argument_validator.IPRouteUtils.get_route_tables_mapping = ( + classmethod( + lambda cls: { + "custom": 200, + "eth": 30400, + } + ) + ) + # the connection index is 0 because there is only one connection profile + # defined here + self.connection_index = 0 + + def tearDown(self): + network_lsr.argument_validator.IPRouteUtils.get_route_tables_mapping = ( + self.old_getter + ) def test_routing_rule_missing_address_family(self): """ @@ -4831,6 +4851,23 @@ class TestValidatorRoutingRules(Python26CompatTestCase): self.test_connections, ) + def test_table_found_when_lookup_named_table(self): + """ + Test that the `validate_route_tables()` will find the table id mapping from + `IPRouteUtils.get_route_tables_mapping()`. + """ + + self.test_connections[0]["ip"]["routing_rule"][0]["table"] = "custom" + + self.validator.validate_route_tables( + self.test_connections[0], + self.connection_index, + ) + self.assertEqual( + self.test_connections[0]["ip"]["routing_rule"][0]["table"], + 200, + ) + class TestValidatorDictBond(Python26CompatTestCase): def setUp(self):