diff options
Diffstat (limited to 'src/op_mode')
-rwxr-xr-x | src/op_mode/clear_dhcp_lease.py | 78 | ||||
-rwxr-xr-x | src/op_mode/connect_disconnect.py | 4 | ||||
-rwxr-xr-x | src/op_mode/conntrack_sync.py | 4 | ||||
-rwxr-xr-x | src/op_mode/firewall.py | 2 | ||||
-rwxr-xr-x | src/op_mode/flow_accounting_op.py | 7 | ||||
-rwxr-xr-x | src/op_mode/generate_ssh_server_key.py | 5 | ||||
-rwxr-xr-x | src/op_mode/openconnect-control.py | 9 | ||||
-rwxr-xr-x | src/op_mode/reset_openvpn.py | 4 | ||||
-rwxr-xr-x | src/op_mode/restart_dhcp_relay.py | 7 | ||||
-rwxr-xr-x | src/op_mode/show_conntrack.py | 102 | ||||
-rwxr-xr-x | src/op_mode/show_nat_rules.py | 9 | ||||
-rwxr-xr-x | src/op_mode/show_nat_translations.py | 16 | ||||
-rwxr-xr-x | src/op_mode/vtysh_wrapper.sh | 5 |
13 files changed, 242 insertions, 10 deletions
diff --git a/src/op_mode/clear_dhcp_lease.py b/src/op_mode/clear_dhcp_lease.py new file mode 100755 index 000000000..250dbcce1 --- /dev/null +++ b/src/op_mode/clear_dhcp_lease.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +import argparse +import re + +from isc_dhcp_leases import Lease +from isc_dhcp_leases import IscDhcpLeases + +from vyos.configquery import ConfigTreeQuery +from vyos.util import ask_yes_no +from vyos.util import call +from vyos.util import commit_in_progress + + +config = ConfigTreeQuery() +base = ['service', 'dhcp-server'] +lease_file = '/config/dhcpd.leases' + + +def del_lease_ip(address): + """ + Read lease_file and write data to this file + without specific section "lease ip" + Delete section "lease x.x.x.x { x;x;x; }" + """ + with open(lease_file, encoding='utf-8') as f: + data = f.read().rstrip() + lease_config_ip = '{(?P<config>[\s\S]+?)\n}' + pattern = rf"lease {address} {lease_config_ip}" + # Delete lease for ip block + data = re.sub(pattern, '', data) + + # Write new data to original lease_file + with open(lease_file, 'w', encoding='utf-8') as f: + f.write(data) + +def is_ip_in_leases(address): + """ + Return True if address found in the lease file + """ + leases = IscDhcpLeases(lease_file) + lease_ips = [] + for lease in leases.get(): + lease_ips.append(lease.ip) + if address not in lease_ips: + print(f'Address "{address}" not found in "{lease_file}"') + return False + return True + + +if not config.exists(base): + print('DHCP-server not configured!') + exit(0) + +if config.exists(base + ['failover']): + print('Lease cannot be reset in failover mode!') + exit(0) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--ip', help='IPv4 address', action='store', required=True) + + args = parser.parse_args() + address = args.ip + + if not is_ip_in_leases(address): + exit(1) + + if commit_in_progress(): + print('Cannot clear DHCP lease while a commit is in progress') + exit(1) + + if not ask_yes_no(f'This will restart DHCP server.\nContinue?'): + exit(1) + else: + del_lease_ip(address) + call('systemctl restart isc-dhcp-server.service') diff --git a/src/op_mode/connect_disconnect.py b/src/op_mode/connect_disconnect.py index ffc574362..936c20bcb 100755 --- a/src/op_mode/connect_disconnect.py +++ b/src/op_mode/connect_disconnect.py @@ -20,6 +20,7 @@ import argparse from psutil import process_iter from vyos.util import call +from vyos.util import commit_in_progress from vyos.util import DEVNULL from vyos.util import is_wwan_connected @@ -87,6 +88,9 @@ def main(): args = parser.parse_args() if args.connect: + if commit_in_progress(): + print('Cannot connect while a commit is in progress') + exit(1) connect(args.connect) elif args.disconnect: disconnect(args.disconnect) diff --git a/src/op_mode/conntrack_sync.py b/src/op_mode/conntrack_sync.py index e45c38f07..54ecd6d0e 100755 --- a/src/op_mode/conntrack_sync.py +++ b/src/op_mode/conntrack_sync.py @@ -22,6 +22,7 @@ from argparse import ArgumentParser from vyos.configquery import CliShellApiConfigQuery from vyos.configquery import ConfigTreeQuery from vyos.util import call +from vyos.util import commit_in_progress from vyos.util import cmd from vyos.util import run from vyos.template import render_to_string @@ -86,6 +87,9 @@ if __name__ == '__main__': if args.restart: is_configured() + if commit_in_progress(): + print('Cannot restart conntrackd while a commit is in progress') + exit(1) syslog.syslog('Restarting conntrack sync service...') cmd('systemctl restart conntrackd.service') diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py index 3146fc357..0aea17b3a 100755 --- a/src/op_mode/firewall.py +++ b/src/op_mode/firewall.py @@ -270,7 +270,7 @@ def show_firewall_group(name=None): references = find_references(group_type, group_name) row = [group_name, group_type, '\n'.join(references) or 'N/A'] if 'address' in group_conf: - row.append("\n".join(sorted(group_conf['address'], key=ipaddress.ip_address))) + row.append("\n".join(sorted(group_conf['address']))) elif 'network' in group_conf: row.append("\n".join(sorted(group_conf['network'], key=ipaddress.ip_network))) elif 'mac_address' in group_conf: diff --git a/src/op_mode/flow_accounting_op.py b/src/op_mode/flow_accounting_op.py index 6586cbceb..514143cd7 100755 --- a/src/op_mode/flow_accounting_op.py +++ b/src/op_mode/flow_accounting_op.py @@ -22,7 +22,9 @@ import ipaddress import os.path from tabulate import tabulate from json import loads -from vyos.util import cmd, run +from vyos.util import cmd +from vyos.util import commit_in_progress +from vyos.util import run from vyos.logger import syslog # some default values @@ -224,6 +226,9 @@ if not _uacctd_running(): # restart pmacct daemon if cmd_args.action == 'restart': + if commit_in_progress(): + print('Cannot restart flow-accounting while a commit is in progress') + exit(1) # run command to restart flow-accounting cmd('systemctl restart uacctd.service', message='Failed to restart flow-accounting') diff --git a/src/op_mode/generate_ssh_server_key.py b/src/op_mode/generate_ssh_server_key.py index cbc9ef973..43e94048d 100755 --- a/src/op_mode/generate_ssh_server_key.py +++ b/src/op_mode/generate_ssh_server_key.py @@ -17,10 +17,15 @@ from sys import exit from vyos.util import ask_yes_no from vyos.util import cmd +from vyos.util import commit_in_progress if not ask_yes_no('Do you really want to remove the existing SSH host keys?'): exit(0) +if commit_in_progress(): + print('Cannot restart SSH while a commit is in progress') + exit(1) + cmd('rm -v /etc/ssh/ssh_host_*') cmd('dpkg-reconfigure openssh-server') cmd('systemctl restart ssh.service') diff --git a/src/op_mode/openconnect-control.py b/src/op_mode/openconnect-control.py index c3cd25186..a128cc011 100755 --- a/src/op_mode/openconnect-control.py +++ b/src/op_mode/openconnect-control.py @@ -19,7 +19,10 @@ import argparse import json from vyos.config import Config -from vyos.util import popen, run, DEVNULL +from vyos.util import commit_in_progress +from vyos.util import popen +from vyos.util import run +from vyos.util import DEVNULL from tabulate import tabulate occtl = '/usr/bin/occtl' @@ -57,6 +60,10 @@ def main(): # Check is Openconnect server configured is_ocserv_configured() + if commit_in_progress(): + print('Cannot restart openconnect while a commit is in progress') + exit(1) + if args.action == "restart": run("sudo systemctl restart ocserv.service") sys.exit(0) diff --git a/src/op_mode/reset_openvpn.py b/src/op_mode/reset_openvpn.py index dbd3eb4d1..efbf65083 100755 --- a/src/op_mode/reset_openvpn.py +++ b/src/op_mode/reset_openvpn.py @@ -17,6 +17,7 @@ import os from sys import argv, exit from vyos.util import call +from vyos.util import commit_in_progress if __name__ == '__main__': if (len(argv) < 1): @@ -25,6 +26,9 @@ if __name__ == '__main__': interface = argv[1] if os.path.isfile(f'/run/openvpn/{interface}.conf'): + if commit_in_progress(): + print('Cannot restart OpenVPN while a commit is in progress') + exit(1) call(f'systemctl restart openvpn@{interface}.service') else: print(f'OpenVPN interface "{interface}" does not exist!') diff --git a/src/op_mode/restart_dhcp_relay.py b/src/op_mode/restart_dhcp_relay.py index af4fb2d15..db5a48970 100755 --- a/src/op_mode/restart_dhcp_relay.py +++ b/src/op_mode/restart_dhcp_relay.py @@ -24,6 +24,7 @@ import os import vyos.config from vyos.util import call +from vyos.util import commit_in_progress parser = argparse.ArgumentParser() @@ -39,6 +40,9 @@ if __name__ == '__main__': if not c.exists_effective('service dhcp-relay'): print("DHCP relay service not configured") else: + if commit_in_progress(): + print('Cannot restart DHCP relay while a commit is in progress') + exit(1) call('systemctl restart isc-dhcp-server.service') sys.exit(0) @@ -47,6 +51,9 @@ if __name__ == '__main__': if not c.exists_effective('service dhcpv6-relay'): print("DHCPv6 relay service not configured") else: + if commit_in_progress(): + print('Cannot restart DHCPv6 relay while commit is in progress') + exit(1) call('systemctl restart isc-dhcp-server6.service') sys.exit(0) diff --git a/src/op_mode/show_conntrack.py b/src/op_mode/show_conntrack.py new file mode 100755 index 000000000..089a3e454 --- /dev/null +++ b/src/op_mode/show_conntrack.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 xmltodict + +from tabulate import tabulate +from vyos.util import cmd + + +def _get_raw_data(): + """ + Get conntrack XML output + """ + return cmd(f'sudo conntrack --dump --output xml') + + +def _xml_to_dict(xml): + """ + Convert XML to dictionary + Return: dictionary + """ + parse = xmltodict.parse(xml) + # If only one conntrack entry we must change dict + if 'meta' in parse['conntrack']['flow']: + return dict(conntrack={'flow': [parse['conntrack']['flow']]}) + return parse + + +def _get_formatted_output(xml): + """ + :param xml: + :return: formatted output + """ + data_entries = [] + dict_data = _xml_to_dict(xml) + for entry in dict_data['conntrack']['flow']: + orig_src, orig_dst, orig_sport, orig_dport = {}, {}, {}, {} + reply_src, reply_dst, reply_sport, reply_dport = {}, {}, {}, {} + proto = {} + for meta in entry['meta']: + direction = meta['@direction'] + if direction in ['original']: + if 'layer3' in meta: + orig_src = meta['layer3']['src'] + orig_dst = meta['layer3']['dst'] + if 'layer4' in meta: + if meta.get('layer4').get('sport'): + orig_sport = meta['layer4']['sport'] + if meta.get('layer4').get('dport'): + orig_dport = meta['layer4']['dport'] + proto = meta['layer4']['@protoname'] + if direction in ['reply']: + if 'layer3' in meta: + reply_src = meta['layer3']['src'] + reply_dst = meta['layer3']['dst'] + if 'layer4' in meta: + if meta.get('layer4').get('sport'): + reply_sport = meta['layer4']['sport'] + if meta.get('layer4').get('dport'): + reply_dport = meta['layer4']['dport'] + proto = meta['layer4']['@protoname'] + if direction == 'independent': + conn_id = meta['id'] + timeout = meta['timeout'] + orig_src = f'{orig_src}:{orig_sport}' if orig_sport else orig_src + orig_dst = f'{orig_dst}:{orig_dport}' if orig_dport else orig_dst + reply_src = f'{reply_src}:{reply_sport}' if reply_sport else reply_src + reply_dst = f'{reply_dst}:{reply_dport}' if reply_dport else reply_dst + state = meta['state'] if 'state' in meta else '' + mark = meta['mark'] + zone = meta['zone'] if 'zone' in meta else '' + data_entries.append( + [conn_id, orig_src, orig_dst, reply_src, reply_dst, proto, state, timeout, mark, zone]) + headers = ["Id", "Original src", "Original dst", "Reply src", "Reply dst", "Protocol", "State", "Timeout", "Mark", + "Zone"] + output = tabulate(data_entries, headers, numalign="left") + return output + + +def show(raw: bool): + conntrack_data = _get_raw_data() + if raw: + return conntrack_data + else: + return _get_formatted_output(conntrack_data) + + +if __name__ == '__main__': + print(show(raw=False)) diff --git a/src/op_mode/show_nat_rules.py b/src/op_mode/show_nat_rules.py index 98adb31dd..60a4bdd13 100755 --- a/src/op_mode/show_nat_rules.py +++ b/src/op_mode/show_nat_rules.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -99,8 +99,9 @@ if args.source or args.destination: if addr_tmp and len_tmp: tran_addr += addr_tmp + '/' + str(len_tmp) + ' ' - if isinstance(tran_addr_json['port'],int): - tran_addr += 'port ' + str(tran_addr_json['port']) + if tran_addr_json.get('port'): + if isinstance(tran_addr_json['port'],int): + tran_addr += 'port ' + str(tran_addr_json['port']) else: if 'masquerade' in data['expr'][i]: @@ -111,6 +112,8 @@ if args.source or args.destination: if srcdest != '': srcdests.append(srcdest) srcdest = '' + else: + srcdests.append('any') print(format_nat_rule.format(rule, srcdests[0], tran_addr, interface)) for i in range(1, len(srcdests)): diff --git a/src/op_mode/show_nat_translations.py b/src/op_mode/show_nat_translations.py index 25091e9fc..508845e23 100755 --- a/src/op_mode/show_nat_translations.py +++ b/src/op_mode/show_nat_translations.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2022 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 @@ -83,11 +83,23 @@ def pipe(): return xml +def xml_to_dict(xml): + """ + Convert XML to dictionary + Return: dictionary + """ + parse = xmltodict.parse(xml) + # If only one NAT entry we must change dict T4499 + if 'meta' in parse['conntrack']['flow']: + return dict(conntrack={'flow': [parse['conntrack']['flow']]}) + return parse + + def process(data, stats, protocol, pipe, verbose, flowtype=''): if not data: return - parsed = xmltodict.parse(data) + parsed = xml_to_dict(data) print(headers(verbose, pipe)) diff --git a/src/op_mode/vtysh_wrapper.sh b/src/op_mode/vtysh_wrapper.sh index 09980e14f..25d09ce77 100755 --- a/src/op_mode/vtysh_wrapper.sh +++ b/src/op_mode/vtysh_wrapper.sh @@ -1,5 +1,6 @@ #!/bin/sh declare -a tmp -# FRR uses ospf6 where we use ospfv3, thus alter the command -tmp=$(echo $@ | sed -e "s/ospfv3/ospf6/") +# FRR uses ospf6 where we use ospfv3, and we use reset over clear for BGP, +# thus alter the commands +tmp=$(echo $@ | sed -e "s/ospfv3/ospf6/" | sed -e "s/^reset bgp/clear bgp/" | sed -e "s/^reset ip bgp/clear ip bgp/") vtysh -c "$tmp" |