diff options
author | Christian Breunig <christian@breunig.cc> | 2023-12-30 23:25:20 +0100 |
---|---|---|
committer | Christian Breunig <christian@breunig.cc> | 2024-01-01 09:25:32 +0100 |
commit | c9eaafd9f808aba8d29be73054e11d37577e539a (patch) | |
tree | aeccfda0a305cf6aca41630900e75bd32961a911 /src/conf_mode/policy-route.py | |
parent | 2078253176046ea4d07e69caeb7932ea439b5614 (diff) | |
download | vyos-1x-c9eaafd9f808aba8d29be73054e11d37577e539a.tar.gz vyos-1x-c9eaafd9f808aba8d29be73054e11d37577e539a.zip |
T5474: establish common file name pattern for XML conf mode commands
We will use _ as CLI level divider. The XML definition filename and also
the Python helper should match the CLI node.
Example:
set interfaces ethernet -> interfaces_ethernet.xml.in
set interfaces bond -> interfaces_bond.xml.in
set service dhcp-server -> service_dhcp-server-xml.in
(cherry picked from commit 4ef110fd2c501b718344c72d495ad7e16d2bd465)
Diffstat (limited to 'src/conf_mode/policy-route.py')
-rwxr-xr-x | src/conf_mode/policy-route.py | 195 |
1 files changed, 0 insertions, 195 deletions
diff --git a/src/conf_mode/policy-route.py b/src/conf_mode/policy-route.py deleted file mode 100755 index adad012de..000000000 --- a/src/conf_mode/policy-route.py +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2021-2023 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# 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, see <http://www.gnu.org/licenses/>. - -import os - -from json import loads -from sys import exit - -from vyos.base import Warning -from vyos.config import Config -from vyos.template import render -from vyos.utils.dict import dict_search_args -from vyos.utils.process import cmd -from vyos.utils.process import run -from vyos import ConfigError -from vyos import airbag -airbag.enable() - -mark_offset = 0x7FFFFFFF -nftables_conf = '/run/nftables_policy.conf' - -valid_groups = [ - 'address_group', - 'domain_group', - 'network_group', - 'port_group', - 'interface_group' -] - -def get_config(config=None): - if config: - conf = config - else: - conf = Config() - base = ['policy'] - - policy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, - no_tag_node_value_mangle=True) - - policy['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True, - no_tag_node_value_mangle=True) - - return policy - -def verify_rule(policy, name, rule_conf, ipv6, rule_id): - icmp = 'icmp' if not ipv6 else 'icmpv6' - if icmp in rule_conf: - icmp_defined = False - if 'type_name' in rule_conf[icmp]: - icmp_defined = True - if 'code' in rule_conf[icmp] or 'type' in rule_conf[icmp]: - raise ConfigError(f'{name} rule {rule_id}: Cannot use ICMP type/code with ICMP type-name') - if 'code' in rule_conf[icmp]: - icmp_defined = True - if 'type' not in rule_conf[icmp]: - raise ConfigError(f'{name} rule {rule_id}: ICMP code can only be defined if ICMP type is defined') - if 'type' in rule_conf[icmp]: - icmp_defined = True - - if icmp_defined and 'protocol' not in rule_conf or rule_conf['protocol'] != icmp: - raise ConfigError(f'{name} rule {rule_id}: ICMP type/code or type-name can only be defined if protocol is ICMP') - - if 'set' in rule_conf: - if 'tcp_mss' in rule_conf['set']: - tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags') - if not tcp_flags or 'syn' not in tcp_flags: - raise ConfigError(f'{name} rule {rule_id}: TCP SYN flag must be set to modify TCP-MSS') - - tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags') - if tcp_flags: - if dict_search_args(rule_conf, 'protocol') != 'tcp': - raise ConfigError('Protocol must be tcp when specifying tcp flags') - - not_flags = dict_search_args(rule_conf, 'tcp', 'flags', 'not') - if not_flags: - duplicates = [flag for flag in tcp_flags if flag in not_flags] - if duplicates: - raise ConfigError(f'Cannot match a tcp flag as set and not set') - - for side in ['destination', 'source']: - if side in rule_conf: - side_conf = rule_conf[side] - - if 'group' in side_conf: - if len({'address_group', 'domain_group', 'network_group'} & set(side_conf['group'])) > 1: - raise ConfigError('Only one address-group, domain-group or network-group can be specified') - - for group in valid_groups: - if group in side_conf['group']: - group_name = side_conf['group'][group] - - if group_name.startswith('!'): - group_name = group_name[1:] - - fw_group = f'ipv6_{group}' if ipv6 and group in ['address_group', 'network_group'] else group - error_group = fw_group.replace("_", "-") - group_obj = dict_search_args(policy['firewall_group'], fw_group, group_name) - - if group_obj is None: - raise ConfigError(f'Invalid {error_group} "{group_name}" on policy route rule') - - if not group_obj: - Warning(f'{error_group} "{group_name}" has no members') - - if 'port' in side_conf or dict_search_args(side_conf, 'group', 'port_group'): - if 'protocol' not in rule_conf: - raise ConfigError('Protocol must be defined if specifying a port or port-group') - - if rule_conf['protocol'] not in ['tcp', 'udp', 'tcp_udp']: - raise ConfigError('Protocol must be tcp, udp, or tcp_udp when specifying a port or port-group') - -def verify(policy): - for route in ['route', 'route6']: - ipv6 = route == 'route6' - if route in policy: - for name, pol_conf in policy[route].items(): - if 'rule' in pol_conf: - for rule_id, rule_conf in pol_conf['rule'].items(): - verify_rule(policy, name, rule_conf, ipv6, rule_id) - - return None - -def generate(policy): - if not os.path.exists(nftables_conf): - policy['first_install'] = True - - render(nftables_conf, 'firewall/nftables-policy.j2', policy) - return None - -def apply_table_marks(policy): - for route in ['route', 'route6']: - if route in policy: - cmd_str = 'ip' if route == 'route' else 'ip -6' - tables = [] - for name, pol_conf in policy[route].items(): - if 'rule' in pol_conf: - for rule_id, rule_conf in pol_conf['rule'].items(): - set_table = dict_search_args(rule_conf, 'set', 'table') - if set_table: - if set_table == 'main': - set_table = '254' - if set_table in tables: - continue - tables.append(set_table) - table_mark = mark_offset - int(set_table) - cmd(f'{cmd_str} rule add pref {set_table} fwmark {table_mark} table {set_table}') - -def cleanup_table_marks(): - for cmd_str in ['ip', 'ip -6']: - json_rules = cmd(f'{cmd_str} -j -N rule list') - rules = loads(json_rules) - for rule in rules: - if 'fwmark' not in rule or 'table' not in rule: - continue - fwmark = rule['fwmark'] - table = int(rule['table']) - if fwmark[:2] == '0x': - fwmark = int(fwmark, 16) - if (int(fwmark) == (mark_offset - table)): - cmd(f'{cmd_str} rule del fwmark {fwmark} table {table}') - -def apply(policy): - install_result = run(f'nft -f {nftables_conf}') - if install_result == 1: - raise ConfigError('Failed to apply policy based routing') - - if 'first_install' not in policy: - cleanup_table_marks() - - apply_table_marks(policy) - - return None - -if __name__ == '__main__': - try: - c = get_config() - verify(c) - generate(c) - apply(c) - except ConfigError as e: - print(e) - exit(1) |