From cfd70c71b131e2a08dfce24eab31e1df4170ccb1 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Sat, 15 Apr 2023 19:03:24 +0200 Subject: [PATCH] Interfaces: Diagnostics: Trace Route: migrate to MVC, closes https://github.com/opnsense/core/issues/6399 --- .../Diagnostics/Api/TracerouteController.php | 55 ++++++ .../Diagnostics/TracerouteController.php | 46 +++++ .../OPNsense/Diagnostics/forms/traceroute.xml | 24 +++ .../mvc/app/models/OPNsense/Core/ACL/ACL.xml | 3 +- .../app/models/OPNsense/Core/Menu/Menu.xml | 2 +- .../OPNsense/Diagnostics/Traceroute.php | 35 ++++ .../OPNsense/Diagnostics/Traceroute.xml | 36 ++++ .../OPNsense/Diagnostics/traceroute.volt | 99 ++++++++++ src/opnsense/scripts/interfaces/traceroute.py | 88 +++++++++ .../conf/actions.d/actions_interface.conf | 6 + src/www/diag_traceroute.php | 186 ------------------ 11 files changed, 392 insertions(+), 188 deletions(-) create mode 100644 src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/TracerouteController.php create mode 100644 src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/TracerouteController.php create mode 100644 src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/forms/traceroute.xml create mode 100644 src/opnsense/mvc/app/models/OPNsense/Diagnostics/Traceroute.php create mode 100644 src/opnsense/mvc/app/models/OPNsense/Diagnostics/Traceroute.xml create mode 100644 src/opnsense/mvc/app/views/OPNsense/Diagnostics/traceroute.volt create mode 100755 src/opnsense/scripts/interfaces/traceroute.py delete mode 100644 src/www/diag_traceroute.php diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/TracerouteController.php b/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/TracerouteController.php new file mode 100644 index 000000000..b6f485781 --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/TracerouteController.php @@ -0,0 +1,55 @@ +getModel(); + $result['result'] = 'ok'; + $result['response'] = json_decode((new Backend())->configdpRun('interface traceroute', [ + (string)$mdl->settings->hostname, + (string)$mdl->settings->ipproto, + (string)$mdl->settings->source_address, + (string)$mdl->settings->protocol + ])); + } + return $result; + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/TracerouteController.php b/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/TracerouteController.php new file mode 100644 index 000000000..144d3d2de --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/TracerouteController.php @@ -0,0 +1,46 @@ +view->pick('OPNsense/Diagnostics/traceroute'); + $this->view->tracerouteForm = $this->getForm("traceroute"); + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/forms/traceroute.xml b/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/forms/traceroute.xml new file mode 100644 index 000000000..c2d1b8b05 --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/forms/traceroute.xml @@ -0,0 +1,24 @@ +
+ + traceroute.settings.hostname + + text + + + + traceroute.settings.ipproto + + dropdown + + + traceroute.settings.protocol + + dropdown + + + traceroute.settings.source_address + + text + + +
diff --git a/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml b/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml index f63940b40..514fd86c6 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Core/ACL/ACL.xml @@ -197,7 +197,8 @@ Diagnostics: Traceroute - diag_traceroute.php* + ui/diagnostics/traceroute* + api/diagnostics/traceroute/* diff --git a/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml b/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml index e017f29a0..25c1553f1 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Core/Menu/Menu.xml @@ -136,7 +136,7 @@ - + diff --git a/src/opnsense/mvc/app/models/OPNsense/Diagnostics/Traceroute.php b/src/opnsense/mvc/app/models/OPNsense/Diagnostics/Traceroute.php new file mode 100644 index 000000000..31a288d41 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/Diagnostics/Traceroute.php @@ -0,0 +1,35 @@ + + :memory: + 1.0.0 + + OPNsense Traceroute Diagnostics + + + + + Y + Provide a valid hostname or address to ping + + + Y + inet + + IPv4 + IPv6 + + + + Y + udp + + UDP + ICMP + + + + N + N + Provide a valid source address + + + + diff --git a/src/opnsense/mvc/app/views/OPNsense/Diagnostics/traceroute.volt b/src/opnsense/mvc/app/views/OPNsense/Diagnostics/traceroute.volt new file mode 100644 index 000000000..a2fc7623f --- /dev/null +++ b/src/opnsense/mvc/app/views/OPNsense/Diagnostics/traceroute.volt @@ -0,0 +1,99 @@ +{# + # Copyright (c) 2023 Deciso B.V. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or withoutmodification, + # are permitted provided that the following conditions are met: + # + # 1. Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # + # 2. Redistributions in binary form must reproduce the above copyright notice, + # this list of conditions and the following disclaimer in the documentation + # and/or other materials provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + # POSSIBILITY OF SUCH DAMAGE. + #} + + + +
+
+ {{ partial("layout_partials/base_form",['fields':tracerouteForm,'id':'frm_TracerouteSettings', 'apply_btn_id':'btn_query'])}} +
+
+
+ + + + + + + + + + + + + + + + + + + +
diff --git a/src/opnsense/scripts/interfaces/traceroute.py b/src/opnsense/scripts/interfaces/traceroute.py new file mode 100755 index 000000000..0e6f60fb8 --- /dev/null +++ b/src/opnsense/scripts/interfaces/traceroute.py @@ -0,0 +1,88 @@ +#!/usr/local/bin/python3 + +""" + Copyright (c) 2023 Ad Schellevis + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +""" +import argparse +import json +import subprocess + +parser = argparse.ArgumentParser() +parser.add_argument('--domain', help='domain name or ip to trace', default='') +parser.add_argument('--ipproto', help='ip protocol version [inet,inet6]', default='inet') +parser.add_argument('--source_address', help='source address to use', default=None) +parser.add_argument('--timeout', help='timeout in seconds', type=int, default=20) +parser.add_argument('--probes', help='number of probes', type=int, default=1) +parser.add_argument('--protocol', help='protocol to use [icmp, udp]', default='udp') +inputargs = parser.parse_args() + + + +result = {'rows': []} +if inputargs.ipproto == 'inet6': + cmd = ['/usr/sbin/traceroute6'] +else: + cmd = ['/usr/sbin/traceroute'] + +cmd = cmd + ['-a', '-w', '2', '-q', '%d' % inputargs.probes] +if inputargs.source_address: + cmd = cmd + ['-s', inputargs.source_address] +if inputargs.protocol.lower() == 'icmp': + cmd.append('-I') + +cmd.append(inputargs.domain) + +proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) +try: + outs, errs = proc.communicate(timeout=inputargs.timeout) +except subprocess.TimeoutExpired: + result['error'] = 'timeout reached' + proc.kill() + outs, errs = proc.communicate() + +if errs: + result['notice'] = errs.strip() + + +for line in outs.strip().split('\n'): + parts = line.split() + if len(parts) < 3: + continue + record = {'address': ''} + if parts[0].startswith('['): + parts = [result['rows'][-1]['ttl'] if len(result['rows']) > 0 else ''] + parts + + record['ttl'] = parts[0] + record['AS'] = parts[1][1:-1] + record['host'] = parts[2] + if parts[3].startswith('('): + record['address'] = parts[3][1:-1] + record['probes'] = ' '.join(parts[4:]) + else: + record['probes'] = ' '.join(parts[3:]) + result['rows'].append(record) + +print(json.dumps(result)) diff --git a/src/opnsense/service/conf/actions.d/actions_interface.conf b/src/opnsense/service/conf/actions.d/actions_interface.conf index e6899117e..3c85014bf 100644 --- a/src/opnsense/service/conf/actions.d/actions_interface.conf +++ b/src/opnsense/service/conf/actions.d/actions_interface.conf @@ -267,3 +267,9 @@ command:/usr/local/opnsense/scripts/interfaces/ping.py parameters: --job %s --detail %s view type:script_output message:View ping jobid %s (%s) + +[traceroute] +command:/usr/local/opnsense/scripts/interfaces/traceroute.py +parameters: --domain %s --ipproto %s --source_address %s --protocol %s +type:script_output +message: traceroute %s diff --git a/src/www/diag_traceroute.php b/src/www/diag_traceroute.php deleted file mode 100644 index 26818e619..000000000 --- a/src/www/diag_traceroute.php +++ /dev/null @@ -1,186 +0,0 @@ - - * Copyright (C) 2016 Deciso B.V. - * Copyright (C) 2005 Paul Taylor - * Copyright (C) 2005 Manuel Kasper - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -require_once("guiconfig.inc"); -require_once("system.inc"); -require_once("interfaces.inc"); - -define('MAX_TTL', 64); - -$cmd_output = false; -if ($_SERVER['REQUEST_METHOD'] === 'GET') { - // set form defaults - $pconfig = array(); - $pconfig['ipproto'] = 'ipv4'; - $pconfig['interface'] = null; - $pconfig['useicmp'] = null; - $pconfig['resolve'] = null; - $pconfig['ttl'] = isset($_GET['ttl']) ? $_GET['ttl'] : 18; - $pconfig['host'] = isset($_GET['host']) ? $_GET['host'] : null; -} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { - // validate input / execute traceroute - $input_errors = array(); - $pconfig = $_POST; - - /* input validation */ - $reqdfields = explode(" ", "host ttl"); - $reqdfieldsn = array(gettext("Host"),gettext("ttl")); - do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors); - - if (!is_numeric($pconfig['ttl']) || $pconfig['ttl'] < 1 || $pconfig['ttl'] > MAX_TTL) { - $input_errors[] = sprintf(gettext("Maximum number of hops must be between 1 and %s"), MAX_TTL); - } - $host = trim($pconfig['host']); - $ipproto = $pconfig['ipproto']; - if ($pconfig['ipproto'] == "ipv4" && is_ipaddrv6($host)) { - $input_errors[] = gettext("When using IPv4, the target host must be an IPv4 address or hostname."); - } elseif ($pconfig['ipproto'] == "ipv6" && is_ipaddrv4($host)) { - $input_errors[] = gettext("When using IPv6, the target host must be an IPv6 address or hostname."); - } - if (count($input_errors) == 0) { - $cmd_args = "-w 2"; - $cmd_args .= !empty($pconfig['useicmp']) ? " -I " : ""; - $cmd_args .= !empty($pconfig['resolve']) ? "" : " -n "; - $cmd_args .= " -m " . escapeshellarg($pconfig['ttl']); - - $command = "/usr/sbin/traceroute"; - - if ($pconfig['ipproto'] == 'ipv6') { - list ($ifaddr) = interfaces_primary_address6($pconfig['interface']); - $command .= '6'; - } else { - list ($ifaddr) = interfaces_primary_address($pconfig['interface']); - } - - if (is_ipaddr($ifaddr) && (is_ipaddr($host) || is_hostname($host))) { - $cmd_args .= " -s " . escapeshellarg($ifaddr) . " "; - } - - $cmd_action = "{$command} {$cmd_args} " . " " . escapeshellarg($host); - $process = proc_open($cmd_action, array(array("pipe", "r"), array("pipe", "w"), array("pipe", "w")), $pipes); - if (is_resource($process)) { - $cmd_output = "# {$cmd_action}\n"; - $cmd_output .= stream_get_contents($pipes[2]); - $cmd_output .= stream_get_contents($pipes[1]); - } - } -} - -legacy_html_escape_form_data($pconfig); -include("head.inc"); -?> - - -
-
-
-
- 0) print_input_errors($input_errors); ?> -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- -
- /> -
- /> -
  - -
-
-
-
-
- -
-
-
- -
-
-
- -