diff options
22 files changed, 210 insertions, 360 deletions
@@ -65,7 +65,6 @@ op_mode_definitions: $(op_xml_obj) rm -f $(OP_TMPL_DIR)/generate/node.def rm -f $(OP_TMPL_DIR)/monitor/node.def rm -f $(OP_TMPL_DIR)/set/node.def - rm -f $(OP_TMPL_DIR)/show/interfaces/node.def rm -f $(OP_TMPL_DIR)/show/node.def rm -f $(OP_TMPL_DIR)/show/system/node.def diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2 index 6cb3b2f95..7a89d29e4 100644 --- a/data/templates/firewall/nftables-policy.j2 +++ b/data/templates/firewall/nftables-policy.j2 @@ -11,7 +11,7 @@ table ip vyos_mangle { type filter hook prerouting priority -150; policy accept; {% if route is vyos_defined %} {% for route_text, conf in route.items() if conf.interface is vyos_defined %} - iifname { {{ ",".join(conf.interface) }} } counter jump VYOS_PBR_{{ route_text }} + iifname { {{ conf.interface | join(",") }} } counter jump VYOS_PBR_{{ route_text }} {% endfor %} {% endif %} } diff --git a/data/templates/https/nginx.default.j2 b/data/templates/https/nginx.default.j2 index d42b3b389..b541ff309 100644 --- a/data/templates/https/nginx.default.j2 +++ b/data/templates/https/nginx.default.j2 @@ -50,6 +50,12 @@ server { {% else %} return 503; {% endif %} +{% if server.allow_client %} +{% for client in server.allow_client %} + allow {{ client }}; +{% endfor %} + deny all; +{% endif %} } error_page 497 =301 https://$host:{{ server.port }}$request_uri; diff --git a/data/templates/sflow/hsflowd.conf.j2 b/data/templates/sflow/hsflowd.conf.j2 index 94f5939be..5000956bd 100644 --- a/data/templates/sflow/hsflowd.conf.j2 +++ b/data/templates/sflow/hsflowd.conf.j2 @@ -28,4 +28,5 @@ sflow { {% if drop_monitor_limit is vyos_defined %} dropmon { limit={{ drop_monitor_limit }} start=on sw=on hw=off } {% endif %} + dbus { } } diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index 624d61759..edbf1e03a 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -349,6 +349,9 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> + <constraint> + #include <include/constraint/interface-name-with-wildcard.xml.in> + </constraint> </properties> <children> <node name="in"> diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index 6adb07598..cf30ab2be 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -60,6 +60,7 @@ <multi/> </properties> </leafNode> + #include <include/allow-client.xml.i> </children> </tagNode> <node name="api" owner="${vyos_conf_scripts_dir}/http-api.py"> diff --git a/interface-definitions/include/allow-client.xml.i b/interface-definitions/include/allow-client.xml.i new file mode 100644 index 000000000..1b06e2c17 --- /dev/null +++ b/interface-definitions/include/allow-client.xml.i @@ -0,0 +1,35 @@ +<!-- include start from allow-client.xml.i --> +<node name="allow-client"> + <properties> + <help>Restrict to allowed IP client addresses</help> + </properties> + <children> + <leafNode name="address"> + <properties> + <help>Allowed IP client addresses</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <valueHelp> + <format>ipv4net</format> + <description>IPv4 address and prefix length</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 address and prefix length</description> + </valueHelp> + <constraint> + <validator name="ip-address"/> + <validator name="ip-cidr"/> + </constraint> + <multi/> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/constraint/interface-name-with-wildcard.xml.in b/interface-definitions/include/constraint/interface-name-with-wildcard.xml.in new file mode 100644 index 000000000..09867b380 --- /dev/null +++ b/interface-definitions/include/constraint/interface-name-with-wildcard.xml.in @@ -0,0 +1,4 @@ +<!-- include start from constraint/interface-name-with-wildcard.xml.in --> +<regex>(bond|br|dum|en|ersp|eth|gnv|ifb|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|veth|vti|vtun|vxlan|wg|wlan|wwan)([0-9]?)(\*?)(.+)?|lo</regex> +<validator name="file-path --lookup-path /sys/class/net --directory"/> +<!-- include end --> diff --git a/interface-definitions/include/generic-interface-broadcast.xml.i b/interface-definitions/include/generic-interface-broadcast.xml.i index 82bfc139b..ac21767d1 100644 --- a/interface-definitions/include/generic-interface-broadcast.xml.i +++ b/interface-definitions/include/generic-interface-broadcast.xml.i @@ -1,7 +1,7 @@ <!-- include start from generic-interface-broadcast.xml.i --> <leafNode name="interface"> <properties> - <help>Interface Name to use</help> + <help>Interface to use</help> <completionHelp> <script>${vyos_completion_dir}/list_interfaces --broadcast</script> </completionHelp> diff --git a/interface-definitions/include/generic-interface-multi-broadcast.xml.i b/interface-definitions/include/generic-interface-multi-broadcast.xml.i index 8160f816d..8b0c38761 100644 --- a/interface-definitions/include/generic-interface-multi-broadcast.xml.i +++ b/interface-definitions/include/generic-interface-multi-broadcast.xml.i @@ -1,7 +1,7 @@ <!-- include start from generic-interface-multi-broadcast.xml.i --> <leafNode name="interface"> <properties> - <help>Interface Name to use</help> + <help>Interface to use</help> <completionHelp> <script>${vyos_completion_dir}/list_interfaces --broadcast</script> </completionHelp> diff --git a/interface-definitions/include/generic-interface-multi-wildcard.xml.i b/interface-definitions/include/generic-interface-multi-wildcard.xml.i new file mode 100644 index 000000000..a93eddbbe --- /dev/null +++ b/interface-definitions/include/generic-interface-multi-wildcard.xml.i @@ -0,0 +1,18 @@ +<!-- include start from generic-interface-multi-wildcard.xml.i --> +<leafNode name="interface"> + <properties> + <help>Interface to use</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name, wildcard (*) supported</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name-with-wildcard.xml.in> + </constraint> + <multi/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in index 287401ed6..558204a06 100644 --- a/interface-definitions/ntp.xml.in +++ b/interface-definitions/ntp.xml.in @@ -57,39 +57,7 @@ </leafNode> </children> </tagNode> - <node name="allow-client"> - <properties> - <help>Specify NTP clients allowed to access the server</help> - </properties> - <children> - <leafNode name="address"> - <properties> - <help>IP address</help> - <valueHelp> - <format>ipv4</format> - <description>Allowed IPv4 address</description> - </valueHelp> - <valueHelp> - <format>ipv4net</format> - <description>Allowed IPv4 prefix</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>Allowed IPv6 address</description> - </valueHelp> - <valueHelp> - <format>ipv6net</format> - <description>Allowed IPv6 prefix</description> - </valueHelp> - <multi/> - <constraint> - <validator name="ip-address"/> - <validator name="ip-prefix"/> - </constraint> - </properties> - </leafNode> - </children> - </node> + #include <include/allow-client.xml.i> #include <include/generic-interface-multi.xml.i> #include <include/listen-address.xml.i> #include <include/interface/vrf.xml.i> diff --git a/interface-definitions/policy-route.xml.in b/interface-definitions/policy-route.xml.in index bbd6dbf56..d4ec75786 100644 --- a/interface-definitions/policy-route.xml.in +++ b/interface-definitions/policy-route.xml.in @@ -12,8 +12,8 @@ </properties> <children> #include <include/generic-description.xml.i> - #include <include/generic-interface-multi.xml.i> #include <include/firewall/enable-default-log.xml.i> + #include <include/generic-interface-multi-wildcard.xml.i> <tagNode name="rule"> <properties> <help>Policy rule number</help> @@ -67,8 +67,8 @@ </properties> <children> #include <include/generic-description.xml.i> - #include <include/generic-interface-multi.xml.i> #include <include/firewall/enable-default-log.xml.i> + #include <include/generic-interface-multi-wildcard.xml.i> <tagNode name="rule"> <properties> <help>Policy rule number</help> diff --git a/op-mode-definitions/include/isis-common.xml.i b/op-mode-definitions/include/isis-common.xml.i index 95a171515..4a2f1e503 100644 --- a/op-mode-definitions/include/isis-common.xml.i +++ b/op-mode-definitions/include/isis-common.xml.i @@ -122,6 +122,12 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> + <leafNode name="prefix-sid"> + <properties> + <help>Show Prefix-SID information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> </children> <command>vtysh -c "show isis route"</command> </node> @@ -136,12 +142,6 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> - <leafNode name="prefix-sids"> - <properties> - <help>Show prefix segment IDs</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> </children> </node> <leafNode name="spf-delay-ietf"> diff --git a/op-mode-definitions/sflow.xml.in b/op-mode-definitions/sflow.xml.in new file mode 100644 index 000000000..9f02dacda --- /dev/null +++ b/op-mode-definitions/sflow.xml.in @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- sflow op mode commands --> +<interfaceDefinition> + <node name="show"> + <children> + <node name="sflow"> + <properties> + <help>Show sFlow statistics</help> + </properties> + <!-- requires sudo, do not remove it --> + <command>sudo ${vyos_op_scripts_dir}/sflow.py show</command> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index e071b7df9..99d3b3ca1 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -198,6 +198,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): def test_ipv4_basic_rules(self): name = 'smoketest' interface = 'eth0' + interface_wc = 'l2tp*' mss_range = '501-1460' conn_mark = '555' @@ -240,6 +241,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name, 'rule', '6', 'connection-mark', conn_mark]) self.cli_set(['firewall', 'interface', interface, 'in', 'name', name]) + self.cli_set(['firewall', 'interface', interface_wc, 'in', 'name', name]) self.cli_commit() @@ -247,6 +249,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ [f'iifname "{interface}"', f'jump NAME_{name}'], + [f'iifname "{interface_wc}"', f'jump NAME_{name}'], ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" log level debug', 'ip ttl 15', 'return'], ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" log level err', 'ip ttl > 102', 'reject'], ['tcp dport 22', 'limit rate 5/minute', 'return'], diff --git a/smoketest/scripts/cli/test_load_balancing_wan.py b/smoketest/scripts/cli/test_load_balancing_wan.py index 0e1806f66..8df3471f7 100755 --- a/smoketest/scripts/cli/test_load_balancing_wan.py +++ b/smoketest/scripts/cli/test_load_balancing_wan.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 @@ -177,7 +177,7 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): nat_vyos_pre_snat_hook = """table ip nat { chain VYOS_PRE_SNAT_HOOK { type nat hook postrouting priority srcnat - 1; policy accept; - return + counter jump WANLOADBALANCE } }""" diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py index 4be36b134..a3df6bf4d 100755 --- a/smoketest/scripts/cli/test_policy_route.py +++ b/smoketest/scripts/cli/test_policy_route.py @@ -26,6 +26,7 @@ conn_mark_set = '111' table_mark_offset = 0x7fffffff table_id = '101' interface = 'eth0' +interface_wc = 'ppp*' interface_ip = '172.16.10.1/24' class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): @@ -236,7 +237,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'set', 'table', table_id]) self.cli_set(['policy', 'route', 'smoketest', 'interface', interface]) - self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface]) + self.cli_set(['policy', 'route', 'smoketest', 'interface', interface_wc]) + self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface_wc]) self.cli_commit() @@ -244,7 +246,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): # IPv4 nftables_search = [ - [f'iifname "{interface}"', 'jump VYOS_PBR_smoketest'], + ['iifname { "' + interface + '", "' + interface_wc + '" }', 'jump VYOS_PBR_smoketest'], ['meta l4proto udp', 'drop'], ['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex], ['ct state new', 'tcp dport 22', 'ip saddr 198.51.100.0/24', 'ip ttl > 2', 'meta mark set ' + mark_hex], @@ -256,7 +258,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): # IPv6 nftables6_search = [ - [f'iifname "{interface}"', 'jump VYOS_PBR6_smoketest'], + [f'iifname "{interface_wc}"', 'jump VYOS_PBR6_smoketest'], ['meta l4proto udp', 'drop'], ['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex], ['ct state new', 'tcp dport 22', 'ip6 saddr 2001:db8::/64', 'ip6 hoplimit > 2', 'meta mark set ' + mark_hex], diff --git a/smoketest/scripts/cli/test_system_sflow.py b/smoketest/scripts/cli/test_system_sflow.py index fef88b56a..1aec050a4 100755 --- a/smoketest/scripts/cli/test_system_sflow.py +++ b/smoketest/scripts/cli/test_system_sflow.py @@ -91,6 +91,7 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase): self.assertIn(f'collector {{ ip = {server} udpport = {port} }}', hsflowd) self.assertIn(f'collector {{ ip = {local_server} udpport = {default_port} }}', hsflowd) self.assertIn(f'dropmon {{ limit={mon_limit} start=on sw=on hw=off }}', hsflowd) + self.assertIn('dbus { }', hsflowd) for interface in Section.interfaces('ethernet'): self.assertIn(f'pcap {{ dev={interface} }}', hsflowd) diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index ce5e63928..b0c38e8d3 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -159,6 +159,8 @@ def generate(https): server_block['port'] = data.get('listen-port', '443') name = data.get('server-name', ['_']) server_block['name'] = name + allow_client = data.get('allow-client', {}) + server_block['allow_client'] = allow_client.get('address', []) server_block_list.append(server_block) # get certificate data diff --git a/src/op_mode/sflow.py b/src/op_mode/sflow.py new file mode 100755 index 000000000..1ff006274 --- /dev/null +++ b/src/op_mode/sflow.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 +# 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 dbus +import sys + +from tabulate import tabulate + +from vyos.configquery import ConfigTreeQuery +from vyos.util import cmd + +import vyos.opmode + + +def _get_raw_sflow(): + bus = dbus.SystemBus() + config = ConfigTreeQuery() + + interfaces = config.values('system sflow interface') + servers = config.list_nodes('system sflow server') + + sflow = bus.get_object('net.sflow.hsflowd', '/net/sflow/hsflowd') + sflow_telemetry = dbus.Interface( + sflow, dbus_interface='net.sflow.hsflowd.telemetry') + agent_address = sflow_telemetry.GetAgent() + samples_dropped = int(sflow_telemetry.Get('dropped_samples')) + samples_packet_sent = int(sflow_telemetry.Get('flow_samples')) + samples_counter_sent = int(sflow_telemetry.Get('counter_samples')) + datagrams_sent = int(sflow_telemetry.Get('datagrams')) + rtmetric_samples = int(sflow_telemetry.Get('rtmetric_samples')) + samples_suppressed = int(sflow_telemetry.Get('flow_samples_suppressed')) + counter_samples_suppressed = int( + sflow_telemetry.Get("counter_samples_suppressed")) + version = sflow_telemetry.GetVersion() + + sflow_dict = { + 'agent_address': agent_address, + 'sflow_interfaces': interfaces, + 'sflow_servers': servers, + 'counter_samples_sent': samples_counter_sent, + 'datagrams_sent': datagrams_sent, + 'packet_samples_dropped': samples_dropped, + 'packet_samples_sent': samples_packet_sent, + 'rtmetric_samples': rtmetric_samples, + 'flow_samples_suppressed': samples_suppressed, + 'counter_samples_suppressed': counter_samples_suppressed, + 'hsflowd_version': version + } + return sflow_dict + + +def _get_formatted_sflow(data): + table = [ + ['Agent address', f'{data.get("agent_address")}'], + ['sFlow interfaces', f'{data.get("sflow_interfaces", "n/a")}'], + ['sFlow servers', f'{data.get("sflow_servers", "n/a")}'], + ['Datagrams sent', f'{data.get("datagrams_sent")}'], + ['Packet samples sent', f'{data.get("packet_samples_sent")}'], + ['Packet samples dropped', f'{data.get("packet_samples_dropped")}'], + ['Counter samples sent', f'{data.get("counter_samples_sent")}'], + ['Flow samples suppressed', f'{data.get("flow_samples_suppressed")}'], + ['Counter samples suppressed', f'{data.get("counter_samples_suppressed")}'] + ] + + return tabulate(table) + + +def show(raw: bool): + + config = ConfigTreeQuery() + if not config.exists('system sflow'): + raise vyos.opmode.UnconfiguredSubsystem( + '"system sflow" is not configured!') + + sflow_data = _get_raw_sflow() + if raw: + return sflow_data + else: + return _get_formatted_sflow(sflow_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) diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py deleted file mode 100755 index eac068274..000000000 --- a/src/op_mode/show_interfaces.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2017-2021 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 os -import re -import sys -import glob -import argparse - -from vyos.ifconfig import Section -from vyos.ifconfig import Interface -from vyos.ifconfig import VRRP -from vyos.util import cmd, call - - -# interfaces = Sections.reserved() -interfaces = ['eno', 'ens', 'enp', 'enx', 'eth', 'vmnet', 'lo', 'tun', 'wan', 'pppoe'] -glob_ifnames = '/sys/class/net/({})*'.format('|'.join(interfaces)) - - -actions = {} -def register(name): - """ - Decorator to register a function into actions with a name. - `actions[name]' can be used to call the registered functions. - We wrap each function in a SIGPIPE handler as all registered functions - can be subject to a broken pipe if there are a lot of interfaces. - """ - def _register(function): - def handled_function(*args, **kwargs): - try: - function(*args, **kwargs) - except BrokenPipeError: - # Flush output to /dev/null and bail out. - os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno()) - sys.exit(1) - actions[name] = handled_function - return handled_function - return _register - - -def filtered_interfaces(ifnames, iftypes, vif, vrrp): - """ - get all the interfaces from the OS and returns them - ifnames can be used to filter which interfaces should be considered - - ifnames: a list of interfaces names to consider, empty do not filter - return an instance of the interface class - """ - if isinstance(iftypes, list): - for iftype in iftypes: - yield from filtered_interfaces(ifnames, iftype, vif, vrrp) - - for ifname in Section.interfaces(iftypes): - # Bail out early if interface name not part of our search list - if ifnames and ifname not in ifnames: - continue - - # As we are only "reading" from the interface - we must use the - # generic base class which exposes all the data via a common API - interface = Interface(ifname, create=False, debug=False) - - # VLAN interfaces have a '.' in their name by convention - if vif and not '.' in ifname: - continue - - if vrrp: - vrrp_interfaces = VRRP.active_interfaces() - if ifname not in vrrp_interfaces: - continue - - yield interface - - -def split_text(text, used=0): - """ - take a string and attempt to split it to fit with the width of the screen - - text: the string to split - used: number of characted already used in the screen - """ - no_tty = call('tty -s') - - returned = cmd('stty size') if not no_tty else '' - if len(returned) == 2: - rows, columns = [int(_) for _ in returned] - else: - rows, columns = (40, 80) - - desc_len = columns - used - - line = '' - for word in text.split(): - if len(line) + len(word) < desc_len: - line = f'{line} {word}' - continue - if line: - yield line[1:] - else: - line = f'{line} {word}' - - yield line[1:] - - -def get_counter_val(clear, now): - """ - attempt to correct a counter if it wrapped, copied from perl - - clear: previous counter - now: the current counter - """ - # This function has to deal with both 32 and 64 bit counters - if clear == 0: - return now - - # device is using 64 bit values assume they never wrap - value = now - clear - if (now >> 32) != 0: - return value - - # The counter has rolled. If the counter has rolled - # multiple times since the clear value, then this math - # is meaningless. - if (value < 0): - value = (4294967296 - clear) + now - - return value - - -@register('help') -def usage(*args): - print(f"Usage: {sys.argv[0]} [intf=NAME|intf-type=TYPE|vif|vrrp] action=ACTION") - print(f" NAME = " + ' | '.join(Section.interfaces())) - print(f" TYPE = " + ' | '.join(Section.sections())) - print(f" ACTION = " + ' | '.join(actions)) - sys.exit(1) - - -@register('allowed') -def run_allowed(**kwarg): - sys.stdout.write(' '.join(Section.interfaces())) - - -def pppoe(ifname): - out = cmd(f'ps -C pppd -f') - if ifname in out: - return 'C' - elif ifname in [_.split('/')[-1] for _ in glob.glob('/etc/ppp/peers/pppoe*')]: - return 'D' - return '' - - -@register('show') -def run_show_intf(ifnames, iftypes, vif, vrrp): - handled = [] - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - handled.append(interface.ifname) - cache = interface.operational.load_counters() - - out = cmd(f'ip addr show {interface.ifname}') - out = re.sub(f'^\d+:\s+','',out) - if re.search('link/tunnel6', out): - tunnel = cmd(f'ip -6 tun show {interface.ifname}') - # tun0: ip/ipv6 remote ::2 local ::1 encaplimit 4 hoplimit 64 tclass inherit flowlabel inherit (flowinfo 0x00000000) - tunnel = re.sub('.*encap', 'encap', tunnel) - out = re.sub('(\n\s+)(link/tunnel6)', f'\g<1>{tunnel}\g<1>\g<2>', out) - - print(out) - - timestamp = int(cache.get('timestamp', 0)) - if timestamp: - when = interface.operational.strtime(timestamp) - print(f' Last clear: {when}') - - description = interface.get_alias() - if description: - print(f' Description: {description}') - - print() - print(interface.operational.formated_stats()) - - for ifname in ifnames: - if ifname not in handled and ifname.startswith('pppoe'): - state = pppoe(ifname) - if not state: - continue - string = { - 'C': 'Coming up', - 'D': 'Link down', - }[state] - print('{}: {}'.format(ifname, string)) - - -@register('show-brief') -def run_show_intf_brief(ifnames, iftypes, vif, vrrp): - format1 = '%-16s %-33s %-4s %s' - format2 = '%-16s %s' - - print('Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down') - print(format1 % ("Interface", "IP Address", "S/L", "Description")) - print(format1 % ("---------", "----------", "---", "-----------")) - - handled = [] - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - handled.append(interface.ifname) - - oper_state = interface.operational.get_state() - admin_state = interface.get_admin_state() - - intf = [interface.ifname,] - - oper = ['u', ] if oper_state in ('up', 'unknown') else ['D', ] - admin = ['u', ] if admin_state in ('up', 'unknown') else ['A', ] - addrs = [_ for _ in interface.get_addr() if not _.startswith('fe80::')] or ['-', ] - descs = list(split_text(interface.get_alias(),0)) - - while intf or oper or admin or addrs or descs: - i = intf.pop(0) if intf else '' - a = addrs.pop(0) if addrs else '' - d = descs.pop(0) if descs else '' - s = [admin.pop(0)] if admin else [] - l = [oper.pop(0)] if oper else [] - if len(a) < 33: - print(format1 % (i, a, '/'.join(s+l), d)) - else: - print(format2 % (i, a)) - print(format1 % ('', '', '/'.join(s+l), d)) - - for ifname in ifnames: - if ifname not in handled and ifname.startswith('pppoe'): - state = pppoe(ifname) - if not state: - continue - string = { - 'C': 'u/D', - 'D': 'A/D', - }[state] - print(format1 % (ifname, '', string, '')) - - -@register('show-count') -def run_show_counters(ifnames, iftypes, vif, vrrp): - formating = '%-12s %10s %10s %10s %10s' - print(formating % ('Interface', 'Rx Packets', 'Rx Bytes', 'Tx Packets', 'Tx Bytes')) - - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - oper = interface.operational.get_state() - - if oper not in ('up','unknown'): - continue - - stats = interface.operational.get_stats() - cache = interface.operational.load_counters() - print(formating % ( - interface.ifname, - get_counter_val(cache['rx_packets'], stats['rx_packets']), - get_counter_val(cache['rx_bytes'], stats['rx_bytes']), - get_counter_val(cache['tx_packets'], stats['tx_packets']), - get_counter_val(cache['tx_bytes'], stats['tx_bytes']), - )) - - -@register('clear') -def run_clear_intf(ifnames, iftypes, vif, vrrp): - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - print(f'Clearing {interface.ifname}') - interface.operational.clear_counters() - - -@register('reset') -def run_reset_intf(ifnames, iftypes, vif, vrrp): - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - interface.operational.reset_counters() - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(add_help=False, description='Show interface information') - parser.add_argument('--intf', action="store", type=str, default='', help='only show the specified interface(s)') - parser.add_argument('--intf-type', action="store", type=str, default='', help='only show the specified interface type') - parser.add_argument('--action', action="store", type=str, default='show', help='action to perform') - parser.add_argument('--vif', action='store_true', default=False, help="only show vif interfaces") - parser.add_argument('--vrrp', action='store_true', default=False, help="only show vrrp interfaces") - parser.add_argument('--help', action='store_true', default=False, help="show help") - - args = parser.parse_args() - - def missing(*args): - print('Invalid action [{args.action}]') - usage() - - actions.get(args.action, missing)( - [_ for _ in args.intf.split(' ') if _], - [_ for _ in args.intf_type.split(' ') if _], - args.vif, - args.vrrp - ) |