diff options
-rw-r--r-- | data/op-mode-standardized.json | 1 | ||||
-rw-r--r-- | op-mode-definitions/nat.xml.in | 13 | ||||
-rwxr-xr-x | src/conf_mode/nat_cgnat.py | 13 | ||||
-rwxr-xr-x | src/op_mode/cgnat.py | 75 |
4 files changed, 102 insertions, 0 deletions
diff --git a/data/op-mode-standardized.json b/data/op-mode-standardized.json index 911869d09..c14133127 100644 --- a/data/op-mode-standardized.json +++ b/data/op-mode-standardized.json @@ -3,6 +3,7 @@ "bgp.py", "bonding.py", "bridge.py", +"cgnat.py", "config_mgmt.py", "conntrack.py", "container.py", diff --git a/op-mode-definitions/nat.xml.in b/op-mode-definitions/nat.xml.in index 307a91337..6398c0e07 100644 --- a/op-mode-definitions/nat.xml.in +++ b/op-mode-definitions/nat.xml.in @@ -7,6 +7,19 @@ <help>Show IPv4 Network Address Translation (NAT) information</help> </properties> <children> + <node name="cgnat"> + <properties> + <help>Show Carrier-Grade Network Address Translation (CGNAT)</help> + </properties> + <children> + <node name="allocation"> + <properties> + <help>Show allocated CGNAT parameters</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/cgnat.py show_allocation</command> + </node> + </children> + </node> <node name="source"> <properties> <help>Show source IPv4 to IPv4 Network Address Translation (NAT) information</help> diff --git a/src/conf_mode/nat_cgnat.py b/src/conf_mode/nat_cgnat.py index 8292f23a4..9a20a3c54 100755 --- a/src/conf_mode/nat_cgnat.py +++ b/src/conf_mode/nat_cgnat.py @@ -203,6 +203,11 @@ def verify(config): f'Range for "{pool} pool {pool_name}" must be defined!' ) + external_pools_query = "keys(pool.external)" + external_pools: list = jmespath.search(external_pools_query, config) + internal_pools_query = "keys(pool.internal)" + internal_pools: list = jmespath.search(internal_pools_query, config) + for rule, rule_config in config['rule'].items(): if 'source' not in rule_config: raise ConfigError(f'Rule "{rule}" source pool must be defined!') @@ -212,6 +217,14 @@ def verify(config): if 'translation' not in rule_config: raise ConfigError(f'Rule "{rule}" translation pool must be defined!') + internal_pool = rule_config['source']['pool'] + if internal_pool not in internal_pools: + raise ConfigError(f'Internal pool "{internal_pool}" does not exist!') + + external_pool = rule_config['translation']['pool'] + if external_pool not in external_pools: + raise ConfigError(f'External pool "{external_pool}" does not exist!') + def generate(config): if not config: diff --git a/src/op_mode/cgnat.py b/src/op_mode/cgnat.py new file mode 100755 index 000000000..a98269a15 --- /dev/null +++ b/src/op_mode/cgnat.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 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 json +import sys +import typing + +from tabulate import tabulate + +import vyos.opmode + +from vyos.configquery import ConfigTreeQuery +from vyos.utils.process import cmd + +CGNAT_TABLE = 'cgnat' + + +def _get_raw_data(): + """ Get CGNAT dictionary + """ + cmd_output = cmd(f'nft --json list table ip {CGNAT_TABLE}') + data = json.loads(cmd_output) + return data + + +def _get_formatted_output(data): + elements = data['nftables'][2]['map']['elem'] + allocations = [] + for elem in elements: + internal = elem[0] # internal + external = elem[1]['concat'][0] # external + start_port = elem[1]['concat'][1]['range'][0] + end_port = elem[1]['concat'][1]['range'][1] + port_range = f'{start_port}-{end_port}' + allocations.append((internal, external, port_range)) + + headers = ['Internal IP', 'External IP', 'Port range'] + output = tabulate(allocations, headers, numalign="left") + return output + + +def show_allocation(raw: bool): + config = ConfigTreeQuery() + if not config.exists('nat cgnat'): + raise vyos.opmode.UnconfiguredSubsystem('CGNAT is not configured') + + if raw: + return _get_raw_data() + + else: + raw_data = _get_raw_data() + return _get_formatted_output(raw_data) + + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print(e) + sys.exit(1) |