From c6c562eca6ff469f603697f7f1d9319b2a5504a3 Mon Sep 17 00:00:00 2001 From: Henning Surmeier Date: Fri, 28 Jan 2022 23:55:06 +0100 Subject: policy: T4219: add local-route(6) incoming-interface --- .../include/interface/inbound-interface.xml.i | 10 ++++ interface-definitions/policy-local-route.xml.in | 2 + smoketest/scripts/cli/test_policy.py | 53 +++++++++++++++++++++- src/conf_mode/policy-local-route.py | 34 ++++++++++++-- 4 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 interface-definitions/include/interface/inbound-interface.xml.i diff --git a/interface-definitions/include/interface/inbound-interface.xml.i b/interface-definitions/include/interface/inbound-interface.xml.i new file mode 100644 index 000000000..5a8d47280 --- /dev/null +++ b/interface-definitions/include/interface/inbound-interface.xml.i @@ -0,0 +1,10 @@ + + + + Inbound Interface + + + + + + diff --git a/interface-definitions/policy-local-route.xml.in b/interface-definitions/policy-local-route.xml.in index 11b1e04d9..573a7963f 100644 --- a/interface-definitions/policy-local-route.xml.in +++ b/interface-definitions/policy-local-route.xml.in @@ -88,6 +88,7 @@ + #include @@ -177,6 +178,7 @@ + #include diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index 73d93c986..491f1766d 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -1206,6 +1206,32 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): self.assertEqual(sort_ip(tmp), sort_ip(original)) + # Test set table for sources with iif + def test_iif_sources_table_id(self): + path = base_path + ['local-route'] + + sources = ['203.0.113.11', '203.0.113.12'] + iif = 'lo' + rule = '100' + table = '150' + + self.cli_set(path + ['rule', rule, 'set', 'table', table]) + self.cli_set(path + ['rule', rule, 'inbound-interface', iif]) + for src in sources: + self.cli_set(path + ['rule', rule, 'source', src]) + + self.cli_commit() + + # Check generated configuration + # Expected values + original = """ + 100: from 203.0.113.11 iif lo lookup 150 + 100: from 203.0.113.12 iif lo lookup 150 + """ + tmp = cmd('ip rule show prio 100') + + self.assertEqual(sort_ip(tmp), sort_ip(original)) + # Test set table for sources and destinations with fwmark def test_fwmark_sources_destination_table_id(self): path = base_path + ['local-route'] @@ -1318,6 +1344,31 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): self.assertEqual(sort_ip(tmp), sort_ip(original)) + # Test set table for sources with iif ipv6 + def test_iif_sources_ipv6_table_id(self): + path = base_path + ['local-route6'] + + sources = ['2001:db8:1338::/126', '2001:db8:1339::/126'] + iif = 'lo' + rule = '102' + table = '150' + for src in sources: + self.cli_set(path + ['rule', rule, 'set', 'table', table]) + self.cli_set(path + ['rule', rule, 'source', src]) + self.cli_set(path + ['rule', rule, 'inbound-interface', iif]) + + self.cli_commit() + + # Check generated configuration + # Expected values + original = """ + 102: from 2001:db8:1338::/126 iif lo lookup 150 + 102: from 2001:db8:1339::/126 iif lo lookup 150 + """ + tmp = cmd('ip -6 rule show prio 102') + + self.assertEqual(sort_ip(tmp), sort_ip(original)) + # Test set table for sources and destinations with fwmark ipv6 def test_fwmark_sources_destination_ipv6_table_id(self): path = base_path + ['local-route6'] @@ -1384,7 +1435,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): 103: from 2001:db8:1338::/126 to 2001:db8:16::/48 fwmark 0x17 lookup 150 103: from 2001:db8:1339::/56 to 2001:db8:13::/48 fwmark 0x17 lookup 150 103: from 2001:db8:1339::/56 to 2001:db8:16::/48 fwmark 0x17 lookup 150 - 103: from 2001:db8:1338::/126 to 2001:db8:13::/48 fwmark 0x17 lookup 150 + 103: from 2001:db8:1338::/126 to 2001:db8:13::/48 fwmark 0x17 lookup 150 """ tmp = cmd('ip rule show prio 103') tmp_v6 = cmd('ip -6 rule show prio 103') diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py index 71183c6ba..0990039c1 100755 --- a/src/conf_mode/policy-local-route.py +++ b/src/conf_mode/policy-local-route.py @@ -18,6 +18,7 @@ import os from sys import exit +from netifaces import interfaces from vyos.config import Config from vyos.configdict import dict_merge from vyos.configdict import node_changed @@ -51,12 +52,15 @@ def get_config(config=None): for rule in (tmp or []): src = leaf_node_changed(conf, base_rule + [rule, 'source']) fwmk = leaf_node_changed(conf, base_rule + [rule, 'fwmark']) + iif = leaf_node_changed(conf, base_rule + [rule, 'inbound-interface']) dst = leaf_node_changed(conf, base_rule + [rule, 'destination']) rule_def = {} if src: rule_def = dict_merge({'source' : src}, rule_def) if fwmk: rule_def = dict_merge({'fwmark' : fwmk}, rule_def) + if iif: + rule_def = dict_merge({'inbound_interface' : iif}, rule_def) if dst: rule_def = dict_merge({'destination' : dst}, rule_def) dict = dict_merge({dict_id : {rule : rule_def}}, dict) @@ -72,6 +76,7 @@ def get_config(config=None): for rule, rule_config in pbr[route]['rule'].items(): src = leaf_node_changed(conf, base_rule + [rule, 'source']) fwmk = leaf_node_changed(conf, base_rule + [rule, 'fwmark']) + iif = leaf_node_changed(conf, base_rule + [rule, 'inbound-interface']) dst = leaf_node_changed(conf, base_rule + [rule, 'destination']) # keep track of changes in configuration # otherwise we might remove an existing node although nothing else has changed @@ -100,6 +105,13 @@ def get_config(config=None): changed = True if len(fwmk) > 0: rule_def = dict_merge({'fwmark' : fwmk}, rule_def) + if iif is None: + if 'inbound_interface' in rule_config: + rule_def = dict_merge({'inbound_interface': rule_config['inbound_interface']}, rule_def) + else: + changed = True + if len(iif) > 0: + rule_def = dict_merge({'inbound_interface' : iif}, rule_def) if dst is None: if 'destination' in rule_config: rule_def = dict_merge({'destination': rule_config['destination']}, rule_def) @@ -125,11 +137,18 @@ def verify(pbr): pbr_route = pbr[route] if 'rule' in pbr_route: for rule in pbr_route['rule']: - if 'source' not in pbr_route['rule'][rule] and 'destination' not in pbr_route['rule'][rule] and 'fwmark' not in pbr_route['rule'][rule]: - raise ConfigError('Source or destination address or fwmark is required!') + if 'source' not in pbr_route['rule'][rule] \ + and 'destination' not in pbr_route['rule'][rule] \ + and 'fwmark' not in pbr_route['rule'][rule] \ + and 'inbound_interface' not in pbr_route['rule'][rule]: + raise ConfigError('Source or destination address or fwmark or inbound-interface is required!') else: if 'set' not in pbr_route['rule'][rule] or 'table' not in pbr_route['rule'][rule]['set']: raise ConfigError('Table set is required!') + if 'inbound_interface' in pbr_route['rule'][rule]: + interface = pbr_route['rule'][rule]['inbound_interface'] + if interface not in interfaces(): + raise ConfigError(f'Interface "{interface}" does not exist') return None @@ -159,7 +178,10 @@ def apply(pbr): rule_config['fwmark'] = rule_config['fwmark'] if 'fwmark' in rule_config else [''] for fwmk in rule_config['fwmark']: f_fwmk = '' if fwmk == '' else f' fwmark {fwmk} ' - call(f'ip{v6} rule del prio {rule} {f_src}{f_dst}{f_fwmk}') + rule_config['inbound_interface'] = rule_config['inbound_interface'] if 'inbound_interface' in rule_config else [''] + for iif in rule_config['inbound_interface']: + f_iif = '' if iif == '' else f' iif {iif} ' + call(f'ip{v6} rule del prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif}') # Generate new config for route in ['local_route', 'local_route6']: @@ -183,7 +205,11 @@ def apply(pbr): if 'fwmark' in rule_config: fwmk = rule_config['fwmark'] f_fwmk = f' fwmark {fwmk} ' - call(f'ip{v6} rule add prio {rule} {f_src}{f_dst}{f_fwmk} lookup {table}') + f_iif = '' + if 'inbound_interface' in rule_config: + iif = rule_config['inbound_interface'] + f_iif = f' iif {iif} ' + call(f'ip{v6} rule add prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif} lookup {table}') return None -- cgit v1.2.3