diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/dns_forwarding.py | 17 | ||||
-rwxr-xr-x | src/conf_mode/service_ipoe-server.py | 97 | ||||
-rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 2 | ||||
-rwxr-xr-x | src/op_mode/accelppp.py | 6 | ||||
-rwxr-xr-x | src/op_mode/dns.py | 4 | ||||
-rwxr-xr-x | src/op_mode/interfaces.py | 6 |
6 files changed, 123 insertions, 9 deletions
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index d0d87d73e..4d6b85d92 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -24,7 +24,7 @@ from vyos.config import Config from vyos.configdict import dict_merge from vyos.hostsd_client import Client as hostsd_client from vyos.template import render -from vyos.template import is_ipv6 +from vyos.template import bracketize_ipv6 from vyos.util import call from vyos.util import chown from vyos.util import dict_search @@ -58,8 +58,16 @@ def get_config(config=None): default_values = defaults(base) # T2665 due to how defaults under tag nodes work, we must clear these out before we merge del default_values['authoritative_domain'] + del default_values['name_server'] dns = dict_merge(default_values, dns) + # T2665: we cleared default values for tag node 'name_server' above. + # We now need to add them back back in a granular way. + if 'name_server' in dns: + default_values = defaults(base + ['name-server']) + for server in dns['name_server']: + dns['name_server'][server] = dict_merge(default_values, dns['name_server'][server]) + # some additions to the default dictionary if 'system' in dns: base_nameservers = ['system', 'name-server'] @@ -329,7 +337,12 @@ def apply(dns): # sources hc.delete_name_servers([hostsd_tag]) if 'name_server' in dns: - hc.add_name_servers({hostsd_tag: dns['name_server']}) + # 'name_server' is a dict of the form + # {'192.0.2.1': {'port': 53}, '2001:db8::1': {'port': 853}, ...} + # canonicalize them as ['192.0.2.1:53', '[2001:db8::1]:853', ...] with IPv6 hosts bracketized + nslist = [(lambda h, p: f"{bracketize_ipv6(h)}:{p['port']}")(h, p) + for (h, p) in dns['name_server'].items()] + hc.add_name_servers({hostsd_tag: nslist}) # delete all nameserver tags hc.delete_name_server_tags_recursor(hc.get_name_server_tags_recursor()) diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py index 4fabe170f..95c72df47 100755 --- a/src/conf_mode/service_ipoe-server.py +++ b/src/conf_mode/service_ipoe-server.py @@ -15,6 +15,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os +import jmespath from sys import exit @@ -29,9 +30,92 @@ from vyos import ConfigError from vyos import airbag airbag.enable() + ipoe_conf = '/run/accel-pppd/ipoe.conf' ipoe_chap_secrets = '/run/accel-pppd/ipoe.chap-secrets' + +def get_pools_in_order(data: dict) -> list: + """Return a list of dictionaries representing pool data in the order + in which they should be allocated. Pool must be defined before we can + use it with 'next-pool' option. + + Args: + data: A dictionary of pool data, where the keys are pool names and the + values are dictionaries containing the 'subnet' key and the optional + 'next_pool' key. + + Returns: + list: A list of dictionaries + + Raises: + ValueError: If a 'next_pool' key references a pool name that + has not been defined. + ValueError: If a circular reference is found in the 'next_pool' keys. + + Example: + config_data = { + ... 'first-pool': { + ... 'next_pool': 'second-pool', + ... 'subnet': '192.0.2.0/25' + ... }, + ... 'second-pool': { + ... 'next_pool': 'third-pool', + ... 'subnet': '203.0.113.0/25' + ... }, + ... 'third-pool': { + ... 'subnet': '198.51.100.0/24' + ... }, + ... 'foo': { + ... 'subnet': '100.64.0.0/24', + ... 'next_pool': 'second-pool' + ... } + ... } + + % get_pools_in_order(config_data) + [{'third-pool': {'subnet': '198.51.100.0/24'}}, + {'second-pool': {'next_pool': 'third-pool', 'subnet': '203.0.113.0/25'}}, + {'first-pool': {'next_pool': 'second-pool', 'subnet': '192.0.2.0/25'}}, + {'foo': {'next_pool': 'second-pool', 'subnet': '100.64.0.0/24'}}] + """ + pools = [] + unresolved_pools = {} + + for pool, pool_config in data.items(): + if 'next_pool' not in pool_config: + pools.insert(0, {pool: pool_config}) + else: + unresolved_pools[pool] = pool_config + + while unresolved_pools: + resolved_pools = [] + + for pool, pool_config in unresolved_pools.items(): + next_pool_name = pool_config['next_pool'] + + if any(p for p in pools if next_pool_name in p): + index = next( + (i for i, p in enumerate(pools) if next_pool_name in p), + None) + pools.insert(index + 1, {pool: pool_config}) + resolved_pools.append(pool) + elif next_pool_name in unresolved_pools: + # next pool not yet resolved + pass + else: + raise ValueError( + f"Pool '{next_pool_name}' not defined in configuration data" + ) + + if not resolved_pools: + raise ValueError("Circular reference in configuration data") + + for pool in resolved_pools: + unresolved_pools.pop(pool) + + return pools + + def get_config(config=None): if config: conf = config @@ -43,6 +127,19 @@ def get_config(config=None): # retrieve common dictionary keys ipoe = get_accel_dict(conf, base, ipoe_chap_secrets) + + if jmespath.search('client_ip_pool.name', ipoe): + dict_named_pools = jmespath.search('client_ip_pool.name', ipoe) + # Multiple named pools require ordered values T5099 + ipoe['ordered_named_pools'] = get_pools_in_order(dict_named_pools) + # T5099 'next-pool' option + if jmespath.search('client_ip_pool.name.*.next_pool', ipoe): + for pool, pool_config in ipoe['client_ip_pool']['name'].items(): + if 'next_pool' in pool_config: + ipoe['first_named_pool'] = pool + ipoe['first_named_pool_subnet'] = pool_config + break + return ipoe diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index d207c63df..63887b278 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -549,6 +549,8 @@ def generate(ipsec): if ipsec['dhcp_no_address']: with open(DHCP_HOOK_IFLIST, 'w') as f: f.write(" ".join(ipsec['dhcp_no_address'].values())) + elif os.path.exists(DHCP_HOOK_IFLIST): + os.unlink(DHCP_HOOK_IFLIST) for path in [swanctl_dir, CERT_PATH, CA_PATH, CRL_PATH, PUBKEY_PATH]: if not os.path.exists(path): diff --git a/src/op_mode/accelppp.py b/src/op_mode/accelppp.py index 87a25bb96..00de45fc8 100755 --- a/src/op_mode/accelppp.py +++ b/src/op_mode/accelppp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-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 @@ -75,8 +75,8 @@ def _get_raw_statistics(accel_output, pattern, protocol): def _get_raw_sessions(port): - cmd_options = 'show sessions ifname,username,ip,ip6,ip6-dp,type,state,' \ - 'uptime-raw,calling-sid,called-sid,sid,comp,rx-bytes-raw,' \ + cmd_options = 'show sessions ifname,username,ip,ip6,ip6-dp,type,rate-limit,' \ + 'state,uptime-raw,calling-sid,called-sid,sid,comp,rx-bytes-raw,' \ 'tx-bytes-raw,rx-pkts,tx-pkts' output = vyos.accel_ppp.accel_cmd(port, cmd_options) parsed_data: list[dict[str, str]] = vyos.accel_ppp.accel_out_parse( diff --git a/src/op_mode/dns.py b/src/op_mode/dns.py index a0e47d7ad..f8863c530 100755 --- a/src/op_mode/dns.py +++ b/src/op_mode/dns.py @@ -17,7 +17,6 @@ import sys -from sys import exit from tabulate import tabulate from vyos.configquery import ConfigTreeQuery @@ -75,8 +74,7 @@ def show_forwarding_statistics(raw: bool): config = ConfigTreeQuery() if not config.exists('service dns forwarding'): - print("DNS forwarding is not configured") - exit(0) + raise vyos.opmode.UnconfiguredSubsystem('DNS forwarding is not configured') dns_data = _get_raw_forwarding_statistics() if raw: diff --git a/src/op_mode/interfaces.py b/src/op_mode/interfaces.py index 678c74980..7d04ec4e3 100755 --- a/src/op_mode/interfaces.py +++ b/src/op_mode/interfaces.py @@ -207,7 +207,11 @@ def _get_raw_data(ifname: typing.Optional[str], res_intf['description'] = interface.get_alias() - res_intf['stats'] = interface.operational.get_stats() + stats = interface.operational.get_stats() + for k in list(stats): + stats[k] = _get_counter_val(cache[k], stats[k]) + + res_intf['stats'] = stats ret.append(res_intf) |