summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/ids/fastnetmon.j211
-rw-r--r--data/templates/ids/fastnetmon_networks_list.j24
-rw-r--r--data/templates/router-advert/radvd.conf.j22
-rw-r--r--debian/vyos-1x.postinst4
-rw-r--r--interface-definitions/policy.xml.in4
-rw-r--r--interface-definitions/service-ids-ddos-protection.xml.in22
-rw-r--r--interface-definitions/service-router-advert.xml.in12
-rw-r--r--op-mode-definitions/ipv4-route.xml.in10
-rw-r--r--op-mode-definitions/ipv6-route.xml.in6
-rw-r--r--op-mode-definitions/monitor-log.xml.in15
-rw-r--r--op-mode-definitions/show-arp.xml.in4
-rw-r--r--op-mode-definitions/show-conntrack.xml.in8
-rw-r--r--op-mode-definitions/show-hardware.xml.in10
-rw-r--r--op-mode-definitions/show-ip.xml.in6
-rw-r--r--op-mode-definitions/show-log.xml.in15
-rw-r--r--op-mode-definitions/show-system.xml.in2
-rw-r--r--op-mode-definitions/show-version.xml.in4
-rw-r--r--python/vyos/cpu.py3
-rw-r--r--python/vyos/opmode.py128
-rwxr-xr-xsmoketest/scripts/cli/test_service_ids.py14
-rwxr-xr-xsmoketest/scripts/cli/test_service_router-advert.py62
-rwxr-xr-xsrc/conf_mode/service_ids_fastnetmon.py56
-rw-r--r--src/etc/systemd/system/fastnetmon.service.d/override.conf12
-rwxr-xr-xsrc/op_mode/conntrack.py (renamed from src/op_mode/show_conntrack.py)43
-rwxr-xr-xsrc/op_mode/cpu.py82
-rwxr-xr-xsrc/op_mode/cpu_summary.py48
-rwxr-xr-xsrc/op_mode/memory.py (renamed from src/op_mode/show_ram.py)39
-rwxr-xr-xsrc/op_mode/neighbor.py (renamed from src/op_mode/show_neigh.py)60
-rw-r--r--src/op_mode/route.py98
-rwxr-xr-xsrc/op_mode/show_cpu.py72
-rwxr-xr-xsrc/op_mode/version.py (renamed from src/op_mode/show_version.py)52
31 files changed, 651 insertions, 257 deletions
diff --git a/data/templates/ids/fastnetmon.j2 b/data/templates/ids/fastnetmon.j2
index c482002fa..005338836 100644
--- a/data/templates/ids/fastnetmon.j2
+++ b/data/templates/ids/fastnetmon.j2
@@ -1,21 +1,22 @@
# enable this option if you want to send logs to local syslog facility
+logging:logging_level = debug
logging:local_syslog_logging = on
# list of all your networks in CIDR format
-networks_list_path = /etc/networks_list
-
-# list networks in CIDR format which will be not monitored for attacks
-white_list_path = /etc/networks_whitelist
+networks_list_path = /run/fastnetmon/networks_list
# Enable/Disable any actions in case of attack
enable_ban = on
+enable_ban_ipv6 = on
## How many packets will be collected from attack traffic
ban_details_records_count = 500
## How long (in seconds) we should keep an IP in blocked state
## If you set 0 here it completely disables unban capability
-ban_time = 1900
+{% if ban_time is vyos_defined %}
+ban_time = {{ ban_time }}
+{% endif %}
# Check if the attack is still active, before triggering an unban callback with this option
# If the attack is still active, check each run of the unban watchdog
diff --git a/data/templates/ids/fastnetmon_networks_list.j2 b/data/templates/ids/fastnetmon_networks_list.j2
index 1c81180be..5f1b3ba4d 100644
--- a/data/templates/ids/fastnetmon_networks_list.j2
+++ b/data/templates/ids/fastnetmon_networks_list.j2
@@ -1,6 +1,4 @@
-{% if network is vyos_defined(var_type=str) %}
-{{ network }}
-{% else %}
+{% if network is vyos_defined() %}
{% for net in network %}
{{ net }}
{% endfor %}
diff --git a/data/templates/router-advert/radvd.conf.j2 b/data/templates/router-advert/radvd.conf.j2
index ed15b32f0..a464795ad 100644
--- a/data/templates/router-advert/radvd.conf.j2
+++ b/data/templates/router-advert/radvd.conf.j2
@@ -50,6 +50,8 @@ interface {{ iface }} {
AdvValidLifetime {{ prefix_options.valid_lifetime }};
AdvOnLink {{ 'off' if prefix_options.no_on_link_flag is vyos_defined else 'on' }};
AdvPreferredLifetime {{ prefix_options.preferred_lifetime }};
+ DeprecatePrefix {{ 'on' if prefix_options.deprecate_prefix is vyos_defined else 'off' }};
+ DecrementLifetimes {{ 'on' if prefix_options.decrement_lifetime is vyos_defined else 'off' }};
};
{% endfor %}
{% endif %}
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index da935bd4c..81121bfe9 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -88,8 +88,10 @@ fi
# Remove unwanted daemon files from /etc
# conntackd
+# pmacct
+# fastnetmon
DELETE="/etc/logrotate.d/conntrackd.distrib /etc/init.d/conntrackd /etc/default/conntrackd
- /etc/default/pmacctd /etc/pmacct"
+ /etc/default/pmacctd /etc/pmacct /etc/networks_list /etc/fastnetmon.conf"
for file in $DELETE; do
if [ -f ${file} ]; then
rm -f ${file}
diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in
index 0d0ada591..15c2beefa 100644
--- a/interface-definitions/policy.xml.in
+++ b/interface-definitions/policy.xml.in
@@ -639,7 +639,7 @@
</leafNode>
<leafNode name="prefix-len">
<properties>
- <help>IP prefix-length to match</help>
+ <help>IP prefix-length to match (cannot be used for BGP routes)</help>
<valueHelp>
<format>u32:0-32</format>
<description>Prefix length</description>
@@ -809,7 +809,7 @@
</leafNode>
<leafNode name="prefix-len">
<properties>
- <help>IPv6 prefix-length to match</help>
+ <help>IPv6 prefix-length to match (cannot be used for BGP routes)</help>
<valueHelp>
<format>u32:0-128</format>
<description>Prefix length</description>
diff --git a/interface-definitions/service-ids-ddos-protection.xml.in b/interface-definitions/service-ids-ddos-protection.xml.in
index 5e65d3106..a176d6fff 100644
--- a/interface-definitions/service-ids-ddos-protection.xml.in
+++ b/interface-definitions/service-ids-ddos-protection.xml.in
@@ -18,6 +18,19 @@
<help>Path to fastnetmon alert script</help>
</properties>
</leafNode>
+ <leafNode name="ban-time">
+ <properties>
+ <help>How long we should keep an IP in blocked state</help>
+ <valueHelp>
+ <format>u32:1-4294967294</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967294"/>
+ </constraint>
+ </properties>
+ <defaultValue>1900</defaultValue>
+ </leafNode>
<leafNode name="direction">
<properties>
<help>Direction for processing traffic</help>
@@ -55,13 +68,18 @@
</node>
<leafNode name="network">
<properties>
- <help>Define monitoring networks</help>
+ <help>Specify IPv4 and IPv6 networks which belong to you</help>
<valueHelp>
<format>ipv4net</format>
- <description>Processed network</description>
+ <description>Your IPv4 prefix(es)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>Your IPv6 prefix(es)</description>
</valueHelp>
<constraint>
<validator name="ipv4-prefix"/>
+ <validator name="ipv6-prefix"/>
</constraint>
<multi/>
</properties>
diff --git a/interface-definitions/service-router-advert.xml.in b/interface-definitions/service-router-advert.xml.in
index 258b7b749..87ec512d6 100644
--- a/interface-definitions/service-router-advert.xml.in
+++ b/interface-definitions/service-router-advert.xml.in
@@ -249,6 +249,18 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="deprecate-prefix">
+ <properties>
+ <help>Upon shutdown, this option will deprecate the prefix by announcing it in the shutdown RA</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="decrement-lifetime">
+ <properties>
+ <help>Lifetime is decremented by the number of seconds since the last RA - use in conjunction with a DHCPv6-PD prefix</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="preferred-lifetime">
<properties>
<help>Time in seconds that the prefix will remain preferred</help>
diff --git a/op-mode-definitions/ipv4-route.xml.in b/op-mode-definitions/ipv4-route.xml.in
index 8f001d5bb..660b34496 100644
--- a/op-mode-definitions/ipv4-route.xml.in
+++ b/op-mode-definitions/ipv4-route.xml.in
@@ -39,7 +39,7 @@
<list>&lt;x.x.x.x&gt;</list>
</completionHelp>
</properties>
- <command>sudo ip neigh flush to "$5"</command>
+ <command>sudo ${vyos_op_scripts_dir}/neighbor.py reset --family inet --address "$5"</command>
</tagNode>
<tagNode name="interface">
<properties>
@@ -48,8 +48,14 @@
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
</properties>
- <command>sudo ip neigh flush dev "$5"</command>
+ <command>sudo ${vyos_op_scripts_dir}/neighbor.py reset --family inet --interface "$5"</command>
</tagNode>
+ <node name="table">
+ <properties>
+ <help>Flush the ARP cache completely</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/neighbor.py reset --family inet</command>
+ </node>
</children>
</node>
<node name="route">
diff --git a/op-mode-definitions/ipv6-route.xml.in b/op-mode-definitions/ipv6-route.xml.in
index 4f8792f9f..d75caf308 100644
--- a/op-mode-definitions/ipv6-route.xml.in
+++ b/op-mode-definitions/ipv6-route.xml.in
@@ -20,7 +20,7 @@
<properties>
<help>Show IPv6 neighbor (NDP) table</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet6</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet6</command>
<children>
<tagNode name="interface">
<properties>
@@ -29,7 +29,7 @@
<script>${vyos_completion_dir}/list_interfaces.py -b</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet6 --interface "$5"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet6 --interface "$5"</command>
</tagNode>
<tagNode name="state">
<properties>
@@ -38,7 +38,7 @@
<list>reachable stale failed permanent</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet6 --state "$5"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet6 --state "$5"</command>
</tagNode>
</children>
</node>
diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in
index f5e0ede59..99d64acb3 100644
--- a/op-mode-definitions/monitor-log.xml.in
+++ b/op-mode-definitions/monitor-log.xml.in
@@ -14,6 +14,19 @@
</properties>
<command>grc journalctl --no-hostname --follow --boot</command>
</node>
+ <node name="ids">
+ <properties>
+ <help>Monitor log for Intrusion Detection System</help>
+ </properties>
+ <children>
+ <leafNode name="ddos-protection">
+ <properties>
+ <help>Monitor last lines of DDOS protection</help>
+ </properties>
+ <command>journalctl --no-hostname --follow --boot --unit fastnetmon.service</command>
+ </leafNode>
+ </children>
+ </node>
<node name="dhcp">
<properties>
<help>Monitor last lines of Dynamic Host Control Protocol (DHCP)</help>
@@ -111,7 +124,7 @@
</node>
<node name="protocol">
<properties>
- <help>Monitor log for Routing Protocols</help>
+ <help>Monitor log for Routing Protocol</help>
</properties>
<children>
<leafNode name="ospf">
diff --git a/op-mode-definitions/show-arp.xml.in b/op-mode-definitions/show-arp.xml.in
index 58cc6e45e..8662549fc 100644
--- a/op-mode-definitions/show-arp.xml.in
+++ b/op-mode-definitions/show-arp.xml.in
@@ -6,7 +6,7 @@
<properties>
<help>Show Address Resolution Protocol (ARP) information</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet</command>
<children>
<tagNode name="interface">
<properties>
@@ -15,7 +15,7 @@
<script>${vyos_completion_dir}/list_interfaces.py -b</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet --interface "$4"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet --interface "$4"</command>
</tagNode>
</children>
</node>
diff --git a/op-mode-definitions/show-conntrack.xml.in b/op-mode-definitions/show-conntrack.xml.in
index 792623d7d..8d921e6a5 100644
--- a/op-mode-definitions/show-conntrack.xml.in
+++ b/op-mode-definitions/show-conntrack.xml.in
@@ -16,7 +16,13 @@
<properties>
<help>Show conntrack entries for IPv4 protocol</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/show_conntrack.py</command>
+ <command>sudo ${vyos_op_scripts_dir}/conntrack.py show --family inet</command>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>Show conntrack entries for IPv6 protocol</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/conntrack.py show --family inet6</command>
</node>
</children>
</node>
diff --git a/op-mode-definitions/show-hardware.xml.in b/op-mode-definitions/show-hardware.xml.in
index 20fdd753d..ebd806ba5 100644
--- a/op-mode-definitions/show-hardware.xml.in
+++ b/op-mode-definitions/show-hardware.xml.in
@@ -9,21 +9,21 @@
<children>
<node name="cpu">
<properties>
- <help>Show CPU info</help>
+ <help>Show CPU informaion</help>
</properties>
- <command>lscpu</command>
+ <command>${vyos_op_scripts_dir}/cpu.py show</command>
<children>
<node name="detail">
<properties>
- <help> Show system CPU details</help>
+ <help>Show system CPU details</help>
</properties>
<command>cat /proc/cpuinfo</command>
</node>
<node name="summary">
<properties>
- <help>Show system CPUs</help>
+ <help>Show system CPUs summary</help>
</properties>
- <command>${vyos_op_scripts_dir}/cpu_summary.py</command>
+ <command>${vyos_op_scripts_dir}/cpu.py show_summary</command>
</node>
</children>
</node>
diff --git a/op-mode-definitions/show-ip.xml.in b/op-mode-definitions/show-ip.xml.in
index d342ac192..0751c50cb 100644
--- a/op-mode-definitions/show-ip.xml.in
+++ b/op-mode-definitions/show-ip.xml.in
@@ -11,7 +11,7 @@
<properties>
<help>Show IPv4 neighbor (ARP) table</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet</command>
<children>
<tagNode name="interface">
<properties>
@@ -20,7 +20,7 @@
<script>${vyos_completion_dir}/list_interfaces.py -b</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet --interface "$5"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet --interface "$5"</command>
</tagNode>
<tagNode name="state">
<properties>
@@ -29,7 +29,7 @@
<list>reachable stale failed permanent</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/show_neigh.py --family inet --state "$5"</command>
+ <command>${vyos_op_scripts_dir}/neighbor.py show --family inet --state "$5"</command>
</tagNode>
</children>
</node>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index 76879e5d6..ab9e128a8 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -32,6 +32,19 @@
</properties>
<command>journalctl --no-hostname --boot --unit conntrackd.service</command>
</leafNode>
+ <node name="ids">
+ <properties>
+ <help>Show log for for Intrusion Detection System</help>
+ </properties>
+ <children>
+ <leafNode name="ddos-protection">
+ <properties>
+ <help>Show log for DDOS protection</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit fastnetmon.service</command>
+ </leafNode>
+ </children>
+ </node>
<node name="dhcp">
<properties>
<help>Show log for Dynamic Host Control Protocol (DHCP)</help>
@@ -243,7 +256,7 @@
</node>
<node name="protocol">
<properties>
- <help>Show log for Routing Protocols</help>
+ <help>Show log for Routing Protocol</help>
</properties>
<children>
<leafNode name="ospf">
diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in
index 68b473bc1..6f05d0c12 100644
--- a/op-mode-definitions/show-system.xml.in
+++ b/op-mode-definitions/show-system.xml.in
@@ -104,7 +104,7 @@
<properties>
<help>Show system memory usage</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_ram.py</command>
+ <command>${vyos_op_scripts_dir}/memory.py show</command>
<children>
<leafNode name="cache">
<properties>
diff --git a/op-mode-definitions/show-version.xml.in b/op-mode-definitions/show-version.xml.in
index 8b7cc7e58..d9c4738af 100644
--- a/op-mode-definitions/show-version.xml.in
+++ b/op-mode-definitions/show-version.xml.in
@@ -6,13 +6,13 @@
<properties>
<help>Show system version information</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/show_version.py</command>
+ <command>sudo ${vyos_op_scripts_dir}/version.py show</command>
<children>
<leafNode name="funny">
<properties>
<help>Show system version and some fun stuff</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/show_version.py --funny</command>
+ <command>sudo ${vyos_op_scripts_dir}/version.py show --funny</command>
</leafNode>
<leafNode name="all">
<properties>
diff --git a/python/vyos/cpu.py b/python/vyos/cpu.py
index a0ef864be..488ae79fb 100644
--- a/python/vyos/cpu.py
+++ b/python/vyos/cpu.py
@@ -32,7 +32,8 @@ import re
def _read_cpuinfo():
with open('/proc/cpuinfo', 'r') as f:
- return f.readlines()
+ lines = f.read().strip()
+ return re.split(r'\n+', lines)
def _split_line(l):
l = l.strip()
diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py
new file mode 100644
index 000000000..0af4359c6
--- /dev/null
+++ b/python/vyos/opmode.py
@@ -0,0 +1,128 @@
+# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import sys
+import typing
+
+
+def _is_op_mode_function_name(name):
+ if re.match(r"^(show|clear|reset|restart)", name):
+ return True
+ else:
+ return False
+
+def _is_show(name):
+ if re.match(r"^show", name):
+ return True
+ else:
+ return False
+
+def _get_op_mode_functions(module):
+ from inspect import getmembers, isfunction
+
+ # Get all functions in that module
+ funcs = getmembers(module, isfunction)
+
+ # getmembers returns (name, func) tuples
+ funcs = list(filter(lambda ft: _is_op_mode_function_name(ft[0]), funcs))
+
+ funcs_dict = {}
+ for (name, thunk) in funcs:
+ funcs_dict[name] = thunk
+
+ return funcs_dict
+
+def _is_optional_type(t):
+ # Optional[t] is internally an alias for Union[t, NoneType]
+ # and there's no easy way to get union members it seems
+ if (type(t) == typing._UnionGenericAlias):
+ if (len(t.__args__) == 2):
+ if t.__args__[1] == type(None):
+ return True
+
+ return False
+
+def _get_arg_type(t):
+ """ Returns the type itself if it's a primitive type,
+ or the "real" type of typing.Optional
+
+ Doesn't work with anything else at the moment!
+ """
+ if _is_optional_type(t):
+ return t.__args__[0]
+ else:
+ return t
+
+def run(module):
+ from argparse import ArgumentParser
+
+ functions = _get_op_mode_functions(module)
+
+ parser = ArgumentParser()
+ subparsers = parser.add_subparsers(dest="subcommand")
+
+ for function_name in functions:
+ subparser = subparsers.add_parser(function_name, help=functions[function_name].__doc__)
+
+ type_hints = typing.get_type_hints(functions[function_name])
+ for opt in type_hints:
+ th = type_hints[opt]
+
+ if _get_arg_type(th) == bool:
+ subparser.add_argument(f"--{opt}", action='store_true')
+ else:
+ if _is_optional_type(th):
+ subparser.add_argument(f"--{opt}", type=_get_arg_type(th), default=None)
+ else:
+ subparser.add_argument(f"--{opt}", type=_get_arg_type(th), required=True)
+
+ # Get options as a dict rather than a namespace,
+ # so that we can modify it and pack for passing to functions
+ args = vars(parser.parse_args())
+
+ if not args["subcommand"]:
+ print("Subcommand required!")
+ parser.print_usage()
+ sys.exit(1)
+
+ function_name = args["subcommand"]
+ func = functions[function_name]
+
+ # Remove the subcommand from the arguments,
+ # it would cause an extra argument error when we pass the dict to a function
+ del args["subcommand"]
+
+ # Show commands must always get the "raw" argument,
+ # but other commands (clear/reset/restart) should not,
+ # because they produce no output and it makes no sense for them.
+ if ("raw" not in args) and _is_show(function_name):
+ args["raw"] = False
+
+ if re.match(r"^show", function_name):
+ # Show commands are slightly special:
+ # they may return human-formatted output
+ # or a raw dict that we need to serialize in JSON for printing
+ res = func(**args)
+ if not args["raw"]:
+ return res
+ else:
+ from json import dumps
+ return dumps(res, indent=4)
+ else:
+ # Other functions should not return anything,
+ # although they may print their own warnings or status messages
+ func(**args)
+
diff --git a/smoketest/scripts/cli/test_service_ids.py b/smoketest/scripts/cli/test_service_ids.py
index 18f1b8ec5..8720362ba 100755
--- a/smoketest/scripts/cli/test_service_ids.py
+++ b/smoketest/scripts/cli/test_service_ids.py
@@ -24,7 +24,8 @@ from vyos.util import process_named_running
from vyos.util import read_file
PROCESS_NAME = 'fastnetmon'
-FASTNETMON_CONF = '/etc/fastnetmon.conf'
+FASTNETMON_CONF = '/run/fastnetmon/fastnetmon.conf'
+NETWORKS_CONF = '/run/fastnetmon/networks_list'
base_path = ['service', 'ids', 'ddos-protection']
class TestServiceIDS(VyOSUnitTestSHIM.TestCase):
@@ -48,7 +49,7 @@ class TestServiceIDS(VyOSUnitTestSHIM.TestCase):
self.assertFalse(process_named_running(PROCESS_NAME))
def test_fastnetmon(self):
- networks = ['10.0.0.0/24', '10.5.5.0/24']
+ networks = ['10.0.0.0/24', '10.5.5.0/24', '2001:db8:10::/64', '2001:db8:20::/64']
interfaces = ['eth0', 'eth1']
fps = '3500'
mbps = '300'
@@ -86,9 +87,18 @@ class TestServiceIDS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'threshold_mbps = {mbps}', config)
self.assertIn(f'ban_for_pps = on', config)
self.assertIn(f'threshold_pps = {pps}', config)
+ # default
+ self.assertIn(f'enable_ban = on', config)
+ self.assertIn(f'enable_ban_ipv6 = on', config)
+ self.assertIn(f'ban_time = 1900', config)
tmp = ','.join(interfaces)
self.assertIn(f'interfaces = {tmp}', config)
+
+ network_config = read_file(NETWORKS_CONF)
+ for tmp in networks:
+ self.assertIn(f'{tmp}', network_config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py
index 1168c05cd..873be7df0 100755
--- a/smoketest/scripts/cli/test_service_router-advert.py
+++ b/smoketest/scripts/cli/test_service_router-advert.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -23,11 +23,13 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.util import read_file
from vyos.util import process_named_running
+PROCESS_NAME = 'radvd'
RADVD_CONF = '/run/radvd/radvd.conf'
interface = 'eth1'
base_path = ['service', 'router-advert', 'interface', interface]
address_base = ['interfaces', 'ethernet', interface, 'address']
+prefix = '::/64'
def get_config_value(key):
tmp = read_file(RADVD_CONF)
@@ -35,18 +37,36 @@ def get_config_value(key):
return tmp[0].split()[0].replace(';','')
class TestServiceRADVD(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(address_base + ['2001:db8::1/64'])
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestServiceRADVD, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, ['service', 'router-advert'])
+
+ cls.cli_set(cls, address_base + ['2001:db8::1/64'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, address_base)
+ super(TestServiceRADVD, cls).tearDownClass()
def tearDown(self):
- self.cli_delete(address_base)
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
self.cli_delete(base_path)
self.cli_commit()
+ # Check for no longer running process
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
def test_common(self):
- self.cli_set(base_path + ['prefix', '::/64', 'no-on-link-flag'])
- self.cli_set(base_path + ['prefix', '::/64', 'no-autonomous-flag'])
- self.cli_set(base_path + ['prefix', '::/64', 'valid-lifetime', 'infinity'])
+ self.cli_set(base_path + ['prefix', prefix, 'no-on-link-flag'])
+ self.cli_set(base_path + ['prefix', prefix, 'no-autonomous-flag'])
+ self.cli_set(base_path + ['prefix', prefix, 'valid-lifetime', 'infinity'])
self.cli_set(base_path + ['other-config-flag'])
# commit changes
@@ -57,7 +77,7 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase):
self.assertEqual(tmp, interface)
tmp = get_config_value('prefix')
- self.assertEqual(tmp, '::/64')
+ self.assertEqual(tmp, prefix)
tmp = get_config_value('AdvOtherConfigFlag')
self.assertEqual(tmp, 'on')
@@ -88,15 +108,19 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value('AdvOnLink')
self.assertEqual(tmp, 'off')
- # Check for running process
- self.assertTrue(process_named_running('radvd'))
+ tmp = get_config_value('DeprecatePrefix')
+ self.assertEqual(tmp, 'off')
+
+ tmp = get_config_value('DecrementLifetimes')
+ self.assertEqual(tmp, 'off')
+
def test_dns(self):
nameserver = ['2001:db8::1', '2001:db8::2']
dnssl = ['vyos.net', 'vyos.io']
ns_lifetime = '599'
- self.cli_set(base_path + ['prefix', '::/64', 'valid-lifetime', 'infinity'])
+ self.cli_set(base_path + ['prefix', prefix, 'valid-lifetime', 'infinity'])
self.cli_set(base_path + ['other-config-flag'])
for ns in nameserver:
@@ -127,5 +151,21 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase):
self.assertIn(tmp, config)
+ def test_deprecate_prefix(self):
+ self.cli_set(base_path + ['prefix', prefix, 'valid-lifetime', 'infinity'])
+ self.cli_set(base_path + ['prefix', prefix, 'deprecate-prefix'])
+ self.cli_set(base_path + ['prefix', prefix, 'decrement-lifetime'])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(RADVD_CONF)
+
+ tmp = get_config_value('DeprecatePrefix')
+ self.assertEqual(tmp, 'on')
+
+ tmp = get_config_value('DecrementLifetimes')
+ self.assertEqual(tmp, 'on')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/service_ids_fastnetmon.py b/src/conf_mode/service_ids_fastnetmon.py
index ae7e582ec..615658c84 100755
--- a/src/conf_mode/service_ids_fastnetmon.py
+++ b/src/conf_mode/service_ids_fastnetmon.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -19,14 +19,16 @@ import os
from sys import exit
from vyos.config import Config
-from vyos import ConfigError
-from vyos.util import call
+from vyos.configdict import dict_merge
from vyos.template import render
+from vyos.util import call
+from vyos.xml import defaults
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
-config_file = r'/etc/fastnetmon.conf'
-networks_list = r'/etc/networks_list'
+config_file = r'/run/fastnetmon/fastnetmon.conf'
+networks_list = r'/run/fastnetmon/networks_list'
def get_config(config=None):
if config:
@@ -34,50 +36,54 @@ def get_config(config=None):
else:
conf = Config()
base = ['service', 'ids', 'ddos-protection']
+ if not conf.exists(base):
+ return None
+
fastnetmon = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+ fastnetmon = dict_merge(default_values, fastnetmon)
+
return fastnetmon
def verify(fastnetmon):
if not fastnetmon:
return None
- if not "mode" in fastnetmon:
- raise ConfigError('ddos-protection mode is mandatory!')
-
- if not "network" in fastnetmon:
- raise ConfigError('Required define network!')
+ if 'mode' not in fastnetmon:
+ raise ConfigError('Specify operating mode!')
- if not "listen_interface" in fastnetmon:
- raise ConfigError('Define listen-interface is mandatory!')
+ if 'listen_interface' not in fastnetmon:
+ raise ConfigError('Specify interface(s) for traffic capture')
- if "alert_script" in fastnetmon:
- if os.path.isfile(fastnetmon["alert_script"]):
+ if 'alert_script' in fastnetmon:
+ if os.path.isfile(fastnetmon['alert_script']):
# Check script permissions
- if not os.access(fastnetmon["alert_script"], os.X_OK):
- raise ConfigError('Script {0} does not have permissions for execution'.format(fastnetmon["alert_script"]))
+ if not os.access(fastnetmon['alert_script'], os.X_OK):
+ raise ConfigError('Script "{alert_script}" is not executable!'.format(fastnetmon['alert_script']))
else:
- raise ConfigError('File {0} does not exists!'.format(fastnetmon["alert_script"]))
+ raise ConfigError('File "{alert_script}" does not exists!'.format(fastnetmon))
def generate(fastnetmon):
if not fastnetmon:
- if os.path.isfile(config_file):
- os.unlink(config_file)
- if os.path.isfile(networks_list):
- os.unlink(networks_list)
+ for file in [config_file, networks_list]:
+ if os.path.isfile(file):
+ os.unlink(file)
- return
+ return None
render(config_file, 'ids/fastnetmon.j2', fastnetmon)
render(networks_list, 'ids/fastnetmon_networks_list.j2', fastnetmon)
-
return None
def apply(fastnetmon):
+ systemd_service = 'fastnetmon.service'
if not fastnetmon:
# Stop fastnetmon service if removed
- call('systemctl stop fastnetmon.service')
+ call(f'systemctl stop {systemd_service}')
else:
- call('systemctl restart fastnetmon.service')
+ call(f'systemctl reload-or-restart {systemd_service}')
return None
diff --git a/src/etc/systemd/system/fastnetmon.service.d/override.conf b/src/etc/systemd/system/fastnetmon.service.d/override.conf
new file mode 100644
index 000000000..8f7f3774f
--- /dev/null
+++ b/src/etc/systemd/system/fastnetmon.service.d/override.conf
@@ -0,0 +1,12 @@
+[Unit]
+RequiresMountsFor=/run
+ConditionPathExists=/run/fastnetmon/fastnetmon.conf
+After=
+After=vyos-router.service
+
+[Service]
+Type=simple
+WorkingDirectory=/run/fastnetmon
+PIDFile=/run/fastnetmon/fastnetmon.pid
+ExecStart=
+ExecStart=/usr/sbin/fastnetmon --configuration_file /run/fastnetmon/fastnetmon.conf
diff --git a/src/op_mode/show_conntrack.py b/src/op_mode/conntrack.py
index 089a3e454..1441d110f 100755
--- a/src/op_mode/show_conntrack.py
+++ b/src/op_mode/conntrack.py
@@ -14,17 +14,21 @@
# 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 sys
import xmltodict
from tabulate import tabulate
from vyos.util import cmd
+from vyos.util import run
+import vyos.opmode
-def _get_raw_data():
+
+def _get_xml_data(family):
"""
Get conntrack XML output
"""
- return cmd(f'sudo conntrack --dump --output xml')
+ return cmd(f'sudo conntrack --dump --family {family} --output xml')
def _xml_to_dict(xml):
@@ -32,26 +36,34 @@ def _xml_to_dict(xml):
Convert XML to dictionary
Return: dictionary
"""
- parse = xmltodict.parse(xml)
+ parse = xmltodict.parse(xml, attr_prefix='')
# 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):
+def _get_raw_data(family):
+ """
+ Return: dictionary
+ """
+ xml = _get_xml_data(family)
+ return _xml_to_dict(xml)
+
+
+def get_formatted_output(dict_data):
"""
:param xml:
:return: formatted output
"""
data_entries = []
- dict_data = _xml_to_dict(xml)
+ #dict_data = _get_raw_data(family)
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']
+ direction = meta['direction']
if direction in ['original']:
if 'layer3' in meta:
orig_src = meta['layer3']['src']
@@ -61,7 +73,7 @@ def _get_formatted_output(xml):
orig_sport = meta['layer4']['sport']
if meta.get('layer4').get('dport'):
orig_dport = meta['layer4']['dport']
- proto = meta['layer4']['@protoname']
+ proto = meta['layer4']['protoname']
if direction in ['reply']:
if 'layer3' in meta:
reply_src = meta['layer3']['src']
@@ -71,7 +83,7 @@ def _get_formatted_output(xml):
reply_sport = meta['layer4']['sport']
if meta.get('layer4').get('dport'):
reply_dport = meta['layer4']['dport']
- proto = meta['layer4']['@protoname']
+ proto = meta['layer4']['protoname']
if direction == 'independent':
conn_id = meta['id']
timeout = meta['timeout']
@@ -90,13 +102,20 @@ def _get_formatted_output(xml):
return output
-def show(raw: bool):
- conntrack_data = _get_raw_data()
+def show(raw: bool, family: str):
+ family = 'ipv6' if family == 'inet6' else 'ipv4'
+ conntrack_data = _get_raw_data(family)
if raw:
return conntrack_data
else:
- return _get_formatted_output(conntrack_data)
+ return get_formatted_output(conntrack_data)
if __name__ == '__main__':
- print(show(raw=False))
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except ValueError as e:
+ print(e)
+ sys.exit(1)
diff --git a/src/op_mode/cpu.py b/src/op_mode/cpu.py
new file mode 100755
index 000000000..f9c425826
--- /dev/null
+++ b/src/op_mode/cpu.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016-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 sys
+
+import vyos.cpu
+import vyos.opmode
+
+from jinja2 import Template
+
+cpu_template = Template("""
+{% for cpu in cpus %}
+{% if 'physical id' in cpu %}CPU socket: {{cpu['physical id']}}{% endif %}
+{% if 'vendor_id' in cpu %}CPU Vendor: {{cpu['vendor_id']}}{% endif %}
+{% if 'model name' in cpu %}Model: {{cpu['model name']}}{% endif %}
+{% if 'cpu cores' in cpu %}Cores: {{cpu['cpu cores']}}{% endif %}
+{% if 'cpu MHz' in cpu %}Current MHz: {{cpu['cpu MHz']}}{% endif %}
+{% endfor %}
+""")
+
+cpu_summary_template = Template("""
+Physical CPU cores: {{count}}
+CPU model(s): {{models | join(", ")}}
+""")
+
+def _get_raw_data():
+ return vyos.cpu.get_cpus()
+
+def _format_cpus(cpu_data):
+ env = {'cpus': cpu_data}
+ return cpu_template.render(env).strip()
+
+def _get_summary_data():
+ count = vyos.cpu.get_core_count()
+ cpu_data = vyos.cpu.get_cpus()
+ models = [c['model name'] for c in cpu_data]
+ env = {'count': count, "models": models}
+
+ return env
+
+def _format_cpu_summary(summary_data):
+ return cpu_summary_template.render(summary_data).strip()
+
+def show(raw: bool):
+ cpu_data = _get_raw_data()
+
+ if raw:
+ return cpu_data
+ else:
+ return _format_cpus(cpu_data)
+
+def show_summary(raw: bool):
+ cpu_summary_data = _get_summary_data()
+
+ if raw:
+ return cpu_summary_data
+ else:
+ return _format_cpu_summary(cpu_summary_data)
+
+
+if __name__ == '__main__':
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except ValueError as e:
+ print(e)
+ sys.exit(1)
+
diff --git a/src/op_mode/cpu_summary.py b/src/op_mode/cpu_summary.py
deleted file mode 100755
index 3bdf5a718..000000000
--- a/src/op_mode/cpu_summary.py
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018-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 re
-from vyos.util import colon_separated_to_dict
-
-FILE_NAME = '/proc/cpuinfo'
-
-def get_raw_data():
- with open(FILE_NAME, 'r') as f:
- data_raw = f.read()
-
- data = colon_separated_to_dict(data_raw)
-
- # Accumulate all data in a dict for future support for machine-readable output
- cpu_data = {}
- cpu_data['cpu_number'] = len(data['processor'])
- cpu_data['models'] = list(set(data['model name']))
-
- # Strip extra whitespace from CPU model names, /proc/cpuinfo is prone to that
- cpu_data['models'] = list(map(lambda s: re.sub(r'\s+', ' ', s), cpu_data['models']))
-
- return cpu_data
-
-def get_formatted_output():
- cpu_data = get_raw_data()
-
- out = "CPU(s): {0}\n".format(cpu_data['cpu_number'])
- out += "CPU model(s): {0}".format(",".join(cpu_data['models']))
-
- return out
-
-if __name__ == '__main__':
- print(get_formatted_output())
-
diff --git a/src/op_mode/show_ram.py b/src/op_mode/memory.py
index 2b0be3965..a3870e498 100755
--- a/src/op_mode/show_ram.py
+++ b/src/op_mode/memory.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 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
@@ -15,7 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-def get_system_memory():
+import sys
+
+import vyos.opmode
+
+
+def _get_system_memory():
from re import search as re_search
def find_value(keyword, mem_data):
@@ -43,10 +48,10 @@ def get_system_memory():
return res
-def get_system_memory_human():
+def _get_system_memory_human():
from vyos.util import bytes_to_human
- mem = get_system_memory()
+ mem = _get_system_memory()
for key in mem:
# The Linux kernel exposes memory values in kilobytes,
@@ -55,17 +60,31 @@ def get_system_memory_human():
return mem
-def get_raw_data():
- return get_system_memory_human()
-
-def get_formatted_output():
- mem = get_raw_data()
+def _get_raw_data():
+ return _get_system_memory_human()
+def _get_formatted_output(mem):
out = "Total: {}\n".format(mem["total"])
out += "Free: {}\n".format(mem["free"])
out += "Used: {}".format(mem["used"])
return out
+def show(raw: bool):
+ ram_data = _get_raw_data()
+
+ if raw:
+ return ram_data
+ else:
+ return _get_formatted_output(ram_data)
+
+
if __name__ == '__main__':
- print(get_formatted_output())
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except ValueError as e:
+ print(e)
+ sys.exit(1)
+
diff --git a/src/op_mode/show_neigh.py b/src/op_mode/neighbor.py
index d874bd544..d86a372ac 100755
--- a/src/op_mode/show_neigh.py
+++ b/src/op_mode/neighbor.py
@@ -28,29 +28,37 @@
# ]
import sys
+import typing
+import vyos.opmode
-def get_raw_data(family, device=None, state=None):
+def interface_exists(interface):
+ import os
+ return os.path.exists(f'/sys/class/net/{interface}')
+
+def get_raw_data(family, interface=None, state=None):
from json import loads
from vyos.util import cmd
- if device:
- device = f"dev {device}"
+ if interface:
+ if not interface_exists(interface):
+ raise ValueError(f"Interface '{interface}' does not exist in the system")
+ interface = f"dev {interface}"
else:
- device = ""
+ interface = ""
if state:
state = f"nud {state}"
else:
state = ""
- neigh_cmd = f"ip --family {family} --json neighbor list {device} {state}"
+ neigh_cmd = f"ip --family {family} --json neighbor list {interface} {state}"
data = loads(cmd(neigh_cmd))
return data
-def get_formatted_output(family, device=None, state=None):
+def format_neighbors(neighs, interface=None):
from tabulate import tabulate
def entry_to_list(e, intf=None):
@@ -68,35 +76,47 @@ def get_formatted_output(family, device=None, state=None):
# Device field is absent from outputs of `ip neigh list dev ...`
if "dev" in e:
dev = e["dev"]
- elif device:
- dev = device
+ elif interface:
+ dev = interface
else:
raise ValueError("interface is not defined")
return [dst, dev, lladdr, state]
- neighs = get_raw_data(family, device=device, state=state)
neighs = map(entry_to_list, neighs)
headers = ["Address", "Interface", "Link layer address", "State"]
return tabulate(neighs, headers)
-if __name__ == '__main__':
- from argparse import ArgumentParser
+def show(raw: bool, family: str, interface: typing.Optional[str], state: typing.Optional[str]):
+ """ Display neighbor table contents """
+ data = get_raw_data(family, interface, state=state)
- parser = ArgumentParser()
- parser.add_argument("-f", "--family", type=str, default="inet", help="Address family")
- parser.add_argument("-i", "--interface", type=str, help="Network interface")
- parser.add_argument("-s", "--state", type=str, help="Neighbor table entry state")
+ if raw:
+ return data
+ else:
+ return format_neighbors(data, interface)
- args = parser.parse_args()
+def reset(family: str, interface: typing.Optional[str], address: typing.Optional[str]):
+ from vyos.util import run
- if args.state:
- if args.state not in ["reachable", "failed", "stale", "permanent"]:
- raise ValueError(f"""Incorrect state "{args.state}"! Must be one of: reachable, stale, failed, permanent""")
+ if address and interface:
+ raise ValueError("interface and address parameters are mutually exclusive")
+ elif address:
+ run(f"""ip --family {family} neighbor flush to {address}""")
+ elif interface:
+ run(f"""ip --family {family} neighbor flush dev {interface}""")
+ else:
+ # Flush an entire neighbor table
+ run(f"""ip --family {family} neighbor flush""")
+
+if __name__ == '__main__':
try:
- print(get_formatted_output(args.family, device=args.interface, state=args.state))
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
except ValueError as e:
print(e)
sys.exit(1)
+
diff --git a/src/op_mode/route.py b/src/op_mode/route.py
new file mode 100644
index 000000000..3bb06adac
--- /dev/null
+++ b/src/op_mode/route.py
@@ -0,0 +1,98 @@
+#!/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/>.
+#
+# Purpose:
+# Displays routing table information.
+# Used by the "run <ip|ipv6> route *" commands.
+
+import re
+import sys
+import typing
+
+from jinja2 import Template
+
+import vyos.opmode
+
+frr_command_template = Template("""
+{% if family == "inet" %}
+ show ip route
+{% else %}
+ show ipv6 route
+{% endif %}
+
+{% if table %}
+ table {{table}}
+{% endif %}
+
+{% if vrf %}
+ vrf {{table}}
+{% endif %}
+
+{% if tag %}
+ tag {{tag}}
+{% elif net %}
+ {{net}}
+{% elif protocol %}
+ {{protocol}}
+{% endif %}
+
+{% if raw %}
+ json
+{% endif %}
+""")
+
+def show(raw: bool,
+ family: str,
+ net: typing.Optional[str],
+ table: typing.Optional[int],
+ protocol: typing.Optional[str],
+ vrf: typing.Optional[str],
+ tag: typing.Optional[str]):
+ if net and protocol:
+ raise ValueError("net and protocol are mutually exclusive")
+ elif table and vrf:
+ raise ValueError("table and vrf are mutually exclusive")
+ elif (family == 'inet6') and (protocol == 'rip'):
+ raise ValueError("rip is not a valid protocol for family inet6")
+ elif (family == 'inet') and (protocol == 'ripng'):
+ raise ValueError("rip is not a valid protocol for family inet6")
+ else:
+ if (family == 'inet6') and (protocol == 'ospf'):
+ protocol = 'ospf6'
+
+ kwargs = dict(locals())
+
+ frr_command = frr_command_template.render(kwargs)
+ frr_command = re.sub(r'\s+', ' ', frr_command)
+
+ from vyos.util import cmd
+ output = cmd(f"vtysh -c '{frr_command}'")
+
+ if raw:
+ from json import loads
+ return loads(output)
+ else:
+ return output
+
+if __name__ == '__main__':
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except ValueError as e:
+ print(e)
+ sys.exit(1)
+
diff --git a/src/op_mode/show_cpu.py b/src/op_mode/show_cpu.py
deleted file mode 100755
index 9973d9789..000000000
--- a/src/op_mode/show_cpu.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2016-2020 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
-
-from jinja2 import Template
-from sys import exit
-from vyos.util import popen, DEVNULL
-
-OUT_TMPL_SRC = """
-{%- if cpu -%}
-{% if 'vendor' in cpu %}CPU Vendor: {{cpu.vendor}}{% endif %}
-{% if 'model' in cpu %}Model: {{cpu.model}}{% endif %}
-{% if 'cpus' in cpu %}Total CPUs: {{cpu.cpus}}{% endif %}
-{% if 'sockets' in cpu %}Sockets: {{cpu.sockets}}{% endif %}
-{% if 'cores' in cpu %}Cores: {{cpu.cores}}{% endif %}
-{% if 'threads' in cpu %}Threads: {{cpu.threads}}{% endif %}
-{% if 'mhz' in cpu %}Current MHz: {{cpu.mhz}}{% endif %}
-{% if 'mhz_min' in cpu %}Minimum MHz: {{cpu.mhz_min}}{% endif %}
-{% if 'mhz_max' in cpu %}Maximum MHz: {{cpu.mhz_max}}{% endif %}
-{%- endif -%}
-"""
-
-def get_raw_data():
- cpu = {}
- cpu_json, code = popen('lscpu -J', stderr=DEVNULL)
-
- if code == 0:
- cpu_info = json.loads(cpu_json)
- if len(cpu_info) > 0 and 'lscpu' in cpu_info:
- for prop in cpu_info['lscpu']:
- if (prop['field'].find('Thread(s)') > -1): cpu['threads'] = prop['data']
- if (prop['field'].find('Core(s)')) > -1: cpu['cores'] = prop['data']
- if (prop['field'].find('Socket(s)')) > -1: cpu['sockets'] = prop['data']
- if (prop['field'].find('CPU(s):')) > -1: cpu['cpus'] = prop['data']
- if (prop['field'].find('CPU MHz')) > -1: cpu['mhz'] = prop['data']
- if (prop['field'].find('CPU min MHz')) > -1: cpu['mhz_min'] = prop['data']
- if (prop['field'].find('CPU max MHz')) > -1: cpu['mhz_max'] = prop['data']
- if (prop['field'].find('Vendor ID')) > -1: cpu['vendor'] = prop['data']
- if (prop['field'].find('Model name')) > -1: cpu['model'] = prop['data']
-
- return cpu
-
-def get_formatted_output():
- cpu = get_raw_data()
-
- tmp = {'cpu':cpu}
- tmpl = Template(OUT_TMPL_SRC)
- return tmpl.render(tmp)
-
-if __name__ == '__main__':
- cpu = get_raw_data()
-
- if len(cpu) > 0:
- print(get_formatted_output())
- else:
- print('CPU information could not be determined\n')
- exit(1)
-
diff --git a/src/op_mode/show_version.py b/src/op_mode/version.py
index b82ab6eca..06208c3e5 100755
--- a/src/op_mode/show_version.py
+++ b/src/op_mode/version.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2016-2020 VyOS maintainers and contributors
+# Copyright (C) 2016-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
@@ -18,13 +18,14 @@
# Displays image version and system information.
# Used by the "run show version" command.
-import argparse
+import sys
+import typing
+
+import vyos.opmode
import vyos.version
import vyos.limericks
from jinja2 import Template
-from sys import exit
-from vyos.util import call
version_output_tmpl = """
Version: VyOS {{version}}
@@ -45,32 +46,39 @@ Hardware S/N: {{hardware_serial}}
Hardware UUID: {{hardware_uuid}}
Copyright: VyOS maintainers and contributors
+{%- if limerick %}
+{{limerick}}
+{% endif -%}
"""
-def get_raw_data():
+def _get_raw_data(funny=False):
version_data = vyos.version.get_full_version_data()
+
+ if funny:
+ version_data["limerick"] = vyos.limericks.get_random()
+
return version_data
-def get_formatted_output():
- version_data = get_raw_data()
+def _get_formatted_output(version_data):
tmpl = Template(version_output_tmpl)
- return tmpl.render(version_data)
+ return tmpl.render(version_data).strip()
-if __name__ == '__main__':
- parser = argparse.ArgumentParser()
- parser.add_argument("-f", "--funny", action="store_true", help="Add something funny to the output")
- parser.add_argument("-j", "--json", action="store_true", help="Produce JSON output")
+def show(raw: bool, funny: typing.Optional[bool]):
+ """ Display neighbor table contents """
+ version_data = _get_raw_data(funny=funny)
- args = parser.parse_args()
+ if raw:
+ return version_data
+ else:
+ return _get_formatted_output(version_data)
- version_data = vyos.version.get_full_version_data()
- if args.json:
- import json
- print(json.dumps(version_data))
- exit(0)
- else:
- print(get_formatted_output())
+if __name__ == '__main__':
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except ValueError as e:
+ print(e)
+ sys.exit(1)
- if args.funny:
- print(vyos.limericks.get_random())