summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/chrony/chrony.conf.j217
-rw-r--r--data/templates/rsyslog/rsyslog.conf.j26
-rw-r--r--debian/control2
-rw-r--r--interface-definitions/service_lldp.xml.in4
-rw-r--r--interface-definitions/service_ntp.xml.in80
-rw-r--r--interface-definitions/system_syslog.xml.in6
-rwxr-xr-x[-rw-r--r--]op-mode-definitions/firewall.xml.in220
-rw-r--r--op-mode-definitions/show-interfaces-wireguard.xml.in2
-rwxr-xr-x[-rw-r--r--]op-mode-definitions/show-log.xml.in125
-rw-r--r--python/vyos/ethtool.py88
-rw-r--r--python/vyos/ifconfig/bond.py3
-rw-r--r--python/vyos/ifconfig/ethernet.py3
-rw-r--r--python/vyos/ifconfig/interface.py3
-rw-r--r--python/vyos/ifconfig/wireguard.py110
-rwxr-xr-xsmoketest/scripts/cli/test_service_ntp.py95
-rwxr-xr-xsrc/activation-scripts/20-ethernet_offload.py13
-rwxr-xr-xsrc/conf_mode/service_ntp.py20
-rwxr-xr-xsrc/conf_mode/system_syslog.py9
-rw-r--r--src/op_mode/interfaces_wireguard.py53
-rwxr-xr-xsrc/services/vyos-configd183
-rwxr-xr-xsrc/services/vyos-http-api-server4
-rw-r--r--src/shim/vyshim.c45
22 files changed, 885 insertions, 206 deletions
diff --git a/data/templates/chrony/chrony.conf.j2 b/data/templates/chrony/chrony.conf.j2
index e3f078fdc..2838f5524 100644
--- a/data/templates/chrony/chrony.conf.j2
+++ b/data/templates/chrony/chrony.conf.j2
@@ -42,7 +42,7 @@ user {{ user }}
{% if config.pool is vyos_defined %}
{% set association = 'pool' %}
{% endif %}
-{{ association }} {{ server | replace('_', '-') }} iburst {{ 'nts' if config.nts is vyos_defined }} {{ 'noselect' if config.noselect is vyos_defined }} {{ 'prefer' if config.prefer is vyos_defined }}
+{{ association }} {{ server | replace('_', '-') }} iburst {{- ' nts' if config.nts is vyos_defined }} {{- ' noselect' if config.noselect is vyos_defined }} {{- ' prefer' if config.prefer is vyos_defined }} {{- ' xleave' if config.interleave is vyos_defined }} {{- ' port ' ~ ptp.port if ptp.port is vyos_defined and config.ptp is vyos_defined }}
{% endfor %}
{% endif %}
@@ -66,3 +66,18 @@ bindaddress {{ address }}
binddevice {{ interface }}
{% endif %}
{% endif %}
+
+{% if ptp.timestamp.interface is vyos_defined %}
+# Enable hardware timestamping on the specified interfaces
+{% for iface, iface_config in ptp.timestamp.interface.items() %}
+{% if iface == "all" %}
+{% set iface = "*" %}
+{% endif %}
+hwtimestamp {{ iface }} {{- ' rxfilter ' ~ iface_config.receive_filter if iface_config.receive_filter is vyos_defined }}
+{% endfor %}
+{% endif %}
+
+{% if ptp.port is vyos_defined %}
+# Enable sending and receiving NTP over PTP packets (PTP transport)
+ptpport {{ ptp.port }}
+{% endif %}
diff --git a/data/templates/rsyslog/rsyslog.conf.j2 b/data/templates/rsyslog/rsyslog.conf.j2
index 97e0ee0b7..effc2ea14 100644
--- a/data/templates/rsyslog/rsyslog.conf.j2
+++ b/data/templates/rsyslog/rsyslog.conf.j2
@@ -54,12 +54,10 @@ $outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archiv
{% endif %}
{% if host_options.protocol is vyos_defined('tcp') %}
{% if host_options.format.octet_counted is vyos_defined %}
-{{ tmp | join(';') }} @@(o){{ host_name | bracketize_ipv6 }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format
-{% else %}
-{{ tmp | join(';') }} @@{{ host_name | bracketize_ipv6 }}:{{ host_options.port }}
+{{ tmp | join(';') }} @@{{ '(o)' if host_options.format.octet_counted is vyos_defined }}{{ host_name | bracketize_ipv6 }}:{{ host_options.port }}{{ ';RSYSLOG_SyslogProtocol23Format' if host_options.format.include_timezone is vyos_defined }}
{% endif %}
{% else %}
-{{ tmp | join(';') }} @{{ host_name | bracketize_ipv6 }}:{{ host_options.port }}{{ ';RSYSLOG_SyslogProtocol23Format' if host_options.format.octet_counted is vyos_defined }}
+{{ tmp | join(';') }} @{{ host_name | bracketize_ipv6 }}:{{ host_options.port }}{{ ';RSYSLOG_SyslogProtocol23Format' if host_options.format.include_timezone is vyos_defined }}
{% endif %}
{% endfor %}
{% endif %}
diff --git a/debian/control b/debian/control
index d1d1602ae..f8cfb876c 100644
--- a/debian/control
+++ b/debian/control
@@ -94,7 +94,7 @@ Depends:
linux-cpupower,
# ipaddrcheck is widely used in IP value validators
ipaddrcheck,
- ethtool,
+ ethtool (>= 6.10),
lm-sensors,
procps,
netplug,
diff --git a/interface-definitions/service_lldp.xml.in b/interface-definitions/service_lldp.xml.in
index 1a06e0cb3..51a9f9cce 100644
--- a/interface-definitions/service_lldp.xml.in
+++ b/interface-definitions/service_lldp.xml.in
@@ -23,6 +23,10 @@
<script>${vyos_completion_dir}/list_interfaces</script>
<list>all</list>
</completionHelp>
+ <constraint>
+ #include <include/constraint/interface-name.xml.i>
+ <regex>all</regex>
+ </constraint>
</properties>
<children>
#include <include/generic-disable-node.xml.i>
diff --git a/interface-definitions/service_ntp.xml.in b/interface-definitions/service_ntp.xml.in
index c057b62b5..5dc0cd295 100644
--- a/interface-definitions/service_ntp.xml.in
+++ b/interface-definitions/service_ntp.xml.in
@@ -13,6 +13,74 @@
#include <include/generic-interface.xml.i>
#include <include/listen-address.xml.i>
#include <include/interface/vrf.xml.i>
+ <node name="ptp">
+ <properties>
+ <help>Enable Precision Time Protocol (PTP) transport</help>
+ </properties>
+ <children>
+ #include <include/port-number.xml.i>
+ <leafNode name="port">
+ <defaultValue>319</defaultValue>
+ </leafNode>
+ <node name="timestamp">
+ <properties>
+ <help>Enable timestamping of packets in the NIC hardware</help>
+ </properties>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Interface to enable timestamping on</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ <list>all</list>
+ </completionHelp>
+ <valueHelp>
+ <format>all</format>
+ <description>Select all interfaces</description>
+ </valueHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/interface-name.xml.i>
+ <regex>all</regex>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="receive-filter">
+ <properties>
+ <help>Selects which inbound packets are timestamped by the NIC</help>
+ <completionHelp>
+ <list>all ntp ptp none</list>
+ </completionHelp>
+ <valueHelp>
+ <format>all</format>
+ <description>All packets are timestamped</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ntp</format>
+ <description>Only NTP packets are timestamped</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ptp</format>
+ <description>Only PTP or NTP packets using the PTP transport are timestamped</description>
+ </valueHelp>
+ <valueHelp>
+ <format>none</format>
+ <description>No packet is timestamped</description>
+ </valueHelp>
+ <constraint>
+ <regex>(all|ntp|ptp|none)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
<leafNode name="leap-second">
<properties>
<help>Leap second behavior</help>
@@ -86,6 +154,18 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="ptp">
+ <properties>
+ <help>Use Precision Time Protocol (PTP) transport for the server</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="interleave">
+ <properties>
+ <help>Use the interleaved mode for the server</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/system_syslog.xml.in b/interface-definitions/system_syslog.xml.in
index 3343e2c59..0a9a00572 100644
--- a/interface-definitions/system_syslog.xml.in
+++ b/interface-definitions/system_syslog.xml.in
@@ -66,6 +66,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="include-timezone">
+ <properties>
+ <help>Include system timezone in syslog message</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</node>
</children>
diff --git a/op-mode-definitions/firewall.xml.in b/op-mode-definitions/firewall.xml.in
index b6ce5bae2..82e6c8668 100644..100755
--- a/op-mode-definitions/firewall.xml.in
+++ b/op-mode-definitions/firewall.xml.in
@@ -98,6 +98,138 @@
</node>
</children>
</node>
+ <node name="input">
+ <properties>
+ <help>Show bridge input firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show bridge input filter firewall ruleset</help>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of bridge input filter firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge input filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of bridge input filter firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge input filter rule</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of specific bridge input filter firewall rule</help>
+ <completionHelp>
+ <path>firewall bridge input filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
+ <node name="output">
+ <properties>
+ <help>Show bridge output firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show bridge output filter firewall ruleset</help>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of bridge output filter firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge output filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of bridge output filter firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge output filter rule</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of specific bridge output filter firewall rule</help>
+ <completionHelp>
+ <path>firewall bridge output filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
+ <node name="prerouting">
+ <properties>
+ <help>Show bridge prerouting firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show bridge prerouting filter firewall ruleset</help>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of bridge prerouting filter firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge prerouting filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of bridge prerouting filter firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge prerouting filter rule</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of specific bridge prerouting filter firewall rule</help>
+ <completionHelp>
+ <path>firewall bridge prerouting filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
<tagNode name="name">
<properties>
<help>Show bridge custom firewall chains</help>
@@ -278,6 +410,50 @@
</node>
</children>
</node>
+ <node name="prerouting">
+ <properties>
+ <help>Show IPv6 prerouting firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="raw">
+ <properties>
+ <help>Show IPv6 prerouting raw firewall ruleset</help>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 prerouting raw firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv6 prerouting raw detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv6 prerouting raw firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv6 prerouting raw rule</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 prerouting raw firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv6 prerouting raw rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
<tagNode name="name">
<properties>
<help>Show IPv6 custom firewall chains</help>
@@ -458,6 +634,50 @@
</node>
</children>
</node>
+ <node name="prerouting">
+ <properties>
+ <help>Show IPv4 prerouting firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="raw">
+ <properties>
+ <help>Show IPv4 prerouting raw firewall ruleset</help>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 prerouting raw firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv4 prerouting raw detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of IPv4 prerouting raw firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv4 prerouting raw rule</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 prerouting raw firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv4 prerouting raw rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
<tagNode name="name">
<properties>
<help>Show IPv4 custom firewall chains</help>
diff --git a/op-mode-definitions/show-interfaces-wireguard.xml.in b/op-mode-definitions/show-interfaces-wireguard.xml.in
index bab7f19c8..0e61ccd74 100644
--- a/op-mode-definitions/show-interfaces-wireguard.xml.in
+++ b/op-mode-definitions/show-interfaces-wireguard.xml.in
@@ -41,7 +41,7 @@
<properties>
<help>Shows current configuration and device information</help>
</properties>
- <command>sudo wg show "$4"</command>
+ <command>sudo ${vyos_op_scripts_dir}/interfaces_wireguard.py show_summary --intf-name="$4"</command>
</leafNode>
</children>
</tagNode>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index f0fad63d2..c2504686d 100644..100755
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -172,6 +172,81 @@
</node>
</children>
</node>
+ <node name="input">
+ <properties>
+ <help>Show Bridge input firewall log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep bri-INP</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show Bridge firewall input filter</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep bri-INP-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall bridge input filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[bri-INP-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="output">
+ <properties>
+ <help>Show Bridge output firewall log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep bri-OUT</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show Bridge firewall output filter</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep bri-OUT-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall bridge output filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[bri-OUT-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="prerouting">
+ <properties>
+ <help>Show Bridge prerouting firewall log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep bri-PRE</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show Bridge firewall prerouting filter</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep bri-PRE-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall bridge prerouting filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[bri-PRE-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
<tagNode name="name">
<properties>
<help>Show custom Bridge firewall log</help>
@@ -295,6 +370,31 @@
</node>
</children>
</node>
+ <node name="prerouting">
+ <properties>
+ <help>Show firewall IPv4 prerouting log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv4-PRE</command>
+ <children>
+ <node name="raw">
+ <properties>
+ <help>Show firewall IPv4 prerouting raw log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv4-PRE-raw</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv4 prerouting raw rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv4-PRE-raw-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
</children>
</node>
<node name="ipv6">
@@ -398,6 +498,31 @@
</node>
</children>
</node>
+ <node name="prerouting">
+ <properties>
+ <help>Show firewall IPv6 prerouting log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv6-PRE</command>
+ <children>
+ <node name="raw">
+ <properties>
+ <help>Show firewall IPv6 prerouting raw log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv6-PRE-raw</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv6 prerouting raw rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv6-PRE-raw-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index 80bb56fa2..21272cc5b 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -56,11 +56,8 @@ class Ethtool:
# '100' : {'full': '', 'half': ''},
# '1000': {'full': ''}
# }
- _speed_duplex = {'auto': {'auto': ''}}
_ring_buffer = None
_driver_name = None
- _auto_negotiation = False
- _auto_negotiation_supported = None
_flow_control = None
def __init__(self, ifname):
@@ -74,56 +71,51 @@ class Ethtool:
self._driver_name = driver.group(1)
# Build a dictinary of supported link-speed and dupley settings.
- out, _ = popen(f'ethtool {ifname}')
- reading = False
- pattern = re.compile(r'\d+base.*')
- for line in out.splitlines()[1:]:
- line = line.lstrip()
- if 'Supported link modes:' in line:
- reading = True
- if 'Supported pause frame use:' in line:
- reading = False
- if reading:
- for block in line.split():
- if pattern.search(block):
- speed = block.split('base')[0]
- duplex = block.split('/')[-1].lower()
- if speed not in self._speed_duplex:
- self._speed_duplex.update({ speed : {}})
- if duplex not in self._speed_duplex[speed]:
- self._speed_duplex[speed].update({ duplex : ''})
- if 'Supports auto-negotiation:' in line:
- # Split the following string: Auto-negotiation: off
- # we are only interested in off or on
- tmp = line.split()[-1]
- self._auto_negotiation_supported = bool(tmp == 'Yes')
- # Only read in if Auto-negotiation is supported
- if self._auto_negotiation_supported and 'Auto-negotiation:' in line:
- # Split the following string: Auto-negotiation: off
- # we are only interested in off or on
- tmp = line.split()[-1]
- self._auto_negotiation = bool(tmp == 'on')
+ # [ {
+ # "ifname": "eth0",
+ # "supported-ports": [ "TP" ],
+ # "supported-link-modes": [ "10baseT/Half","10baseT/Full","100baseT/Half","100baseT/Full","1000baseT/Full" ],
+ # "supported-pause-frame-use": "Symmetric",
+ # "supports-auto-negotiation": true,
+ # "supported-fec-modes": [ ],
+ # "advertised-link-modes": [ "10baseT/Half","10baseT/Full","100baseT/Half","100baseT/Full","1000baseT/Full" ],
+ # "advertised-pause-frame-use": "Symmetric",
+ # "advertised-auto-negotiation": true,
+ # "advertised-fec-modes": [ ],
+ # "speed": 1000,
+ # "duplex": "Full",
+ # "auto-negotiation": false,
+ # "port": "Twisted Pair",
+ # "phyad": 1,
+ # "transceiver": "internal",
+ # "supports-wake-on": "pumbg",
+ # "wake-on": "g",
+ # "current-message-level": 7,
+ # "link-detected": true
+ # } ]
+ out, _ = popen(f'ethtool --json {ifname}')
+ self._base_settings = loads(out)[0]
# Now populate driver features
out, _ = popen(f'ethtool --json --show-features {ifname}')
- self._features = loads(out)
+ self._features = loads(out)[0]
# Get information about NIC ring buffers
out, _ = popen(f'ethtool --json --show-ring {ifname}')
- self._ring_buffer = loads(out)
+ self._ring_buffer = loads(out)[0]
# Get current flow control settings, but this is not supported by
# all NICs (e.g. vmxnet3 does not support is)
out, err = popen(f'ethtool --json --show-pause {ifname}')
if not bool(err):
- self._flow_control = loads(out)
+ self._flow_control = loads(out)[0]
def check_auto_negotiation_supported(self):
""" Check if the NIC supports changing auto-negotiation """
- return self._auto_negotiation_supported
+ return self._base_settings['supports-auto-negotiation']
def get_auto_negotiation(self):
- return self._auto_negotiation_supported and self._auto_negotiation
+ return self._base_settings['supports-auto-negotiation'] and self._base_settings['auto-negotiation']
def get_driver_name(self):
return self._driver_name
@@ -137,9 +129,9 @@ class Ethtool:
"""
active = False
fixed = True
- if feature in self._features[0]:
- active = bool(self._features[0][feature]['active'])
- fixed = bool(self._features[0][feature]['fixed'])
+ if feature in self._features:
+ active = bool(self._features[feature]['active'])
+ fixed = bool(self._features[feature]['fixed'])
return active, fixed
def get_generic_receive_offload(self):
@@ -165,14 +157,14 @@ class Ethtool:
# thus when it's impossible return None
if rx_tx not in ['rx', 'tx']:
ValueError('Ring-buffer type must be either "rx" or "tx"')
- return str(self._ring_buffer[0].get(f'{rx_tx}-max', None))
+ return str(self._ring_buffer.get(f'{rx_tx}-max', None))
def get_ring_buffer(self, rx_tx):
# Configuration of RX/TX ring-buffers is not supported on every device,
# thus when it's impossible return None
if rx_tx not in ['rx', 'tx']:
ValueError('Ring-buffer type must be either "rx" or "tx"')
- return str(self._ring_buffer[0].get(rx_tx, None))
+ return str(self._ring_buffer.get(rx_tx, None))
def check_speed_duplex(self, speed, duplex):
""" Check if the passed speed and duplex combination is supported by
@@ -184,12 +176,16 @@ class Ethtool:
if duplex not in ['auto', 'full', 'half']:
raise ValueError(f'Value "{duplex}" for duplex is invalid!')
+ if speed == 'auto' and duplex == 'auto':
+ return True
+
if self.get_driver_name() in _drivers_without_speed_duplex_flow:
return False
- if speed in self._speed_duplex:
- if duplex in self._speed_duplex[speed]:
- return True
+ # ['10baset/half', '10baset/full', '100baset/half', '100baset/full', '1000baset/full']
+ tmp = [x.lower() for x in self._base_settings['supported-link-modes']]
+ if f'{speed}baset/{duplex}' in tmp:
+ return True
return False
def check_flow_control(self):
@@ -201,4 +197,4 @@ class Ethtool:
raise ValueError('Interface does not support changing '\
'flow-control settings!')
- return 'on' if bool(self._flow_control[0]['autonegotiate']) else 'off'
+ return 'on' if bool(self._flow_control['autonegotiate']) else 'off'
diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py
index b8ea90049..8ba481728 100644
--- a/python/vyos/ifconfig/bond.py
+++ b/python/vyos/ifconfig/bond.py
@@ -504,3 +504,6 @@ class BondIf(Interface):
# call base class first
super().update(config)
+
+ # enable/disable EAPoL (Extensible Authentication Protocol over Local Area Network)
+ self.set_eapol()
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 8d96c863f..61da7b74b 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -452,3 +452,6 @@ class EthernetIf(Interface):
# call base class last
super().update(config)
+
+ # enable/disable EAPoL (Extensible Authentication Protocol over Local Area Network)
+ self.set_eapol()
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 31fcf6ca6..002d3da9e 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -1813,9 +1813,6 @@ class Interface(Control):
value = '1' if (tmp != None) else '0'
self.set_per_client_thread(value)
- # enable/disable EAPoL (Extensible Authentication Protocol over Local Area Network)
- self.set_eapol()
-
# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
# function. We will only enable the interface if 'up' was called as
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index 5b5f25323..9030b1302 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -26,6 +26,7 @@ from vyos.ifconfig import Interface
from vyos.ifconfig import Operational
from vyos.template import is_ipv6
+
class WireGuardOperational(Operational):
def _dump(self):
"""Dump wireguard data in a python friendly way."""
@@ -54,7 +55,17 @@ class WireGuardOperational(Operational):
}
else:
# We are entering a peer
- device, public_key, preshared_key, endpoint, allowed_ips, latest_handshake, transfer_rx, transfer_tx, persistent_keepalive = items
+ (
+ device,
+ public_key,
+ preshared_key,
+ endpoint,
+ allowed_ips,
+ latest_handshake,
+ transfer_rx,
+ transfer_tx,
+ persistent_keepalive,
+ ) = items
if allowed_ips == '(none)':
allowed_ips = []
else:
@@ -72,75 +83,78 @@ class WireGuardOperational(Operational):
def show_interface(self):
from vyos.config import Config
+
c = Config()
wgdump = self._dump().get(self.config['ifname'], None)
- c.set_level(["interfaces", "wireguard", self.config['ifname']])
- description = c.return_effective_value(["description"])
- ips = c.return_effective_values(["address"])
+ c.set_level(['interfaces', 'wireguard', self.config['ifname']])
+ description = c.return_effective_value(['description'])
+ ips = c.return_effective_values(['address'])
- answer = "interface: {}\n".format(self.config['ifname'])
- if (description):
- answer += " description: {}\n".format(description)
- if (ips):
- answer += " address: {}\n".format(", ".join(ips))
+ answer = 'interface: {}\n'.format(self.config['ifname'])
+ if description:
+ answer += ' description: {}\n'.format(description)
+ if ips:
+ answer += ' address: {}\n'.format(', '.join(ips))
- answer += " public key: {}\n".format(wgdump['public_key'])
- answer += " private key: (hidden)\n"
- answer += " listening port: {}\n".format(wgdump['listen_port'])
- answer += "\n"
+ answer += ' public key: {}\n'.format(wgdump['public_key'])
+ answer += ' private key: (hidden)\n'
+ answer += ' listening port: {}\n'.format(wgdump['listen_port'])
+ answer += '\n'
- for peer in c.list_effective_nodes(["peer"]):
+ for peer in c.list_effective_nodes(['peer']):
if wgdump['peers']:
- pubkey = c.return_effective_value(["peer", peer, "public_key"])
+ pubkey = c.return_effective_value(['peer', peer, 'public-key'])
if pubkey in wgdump['peers']:
wgpeer = wgdump['peers'][pubkey]
- answer += " peer: {}\n".format(peer)
- answer += " public key: {}\n".format(pubkey)
+ answer += ' peer: {}\n'.format(peer)
+ answer += ' public key: {}\n'.format(pubkey)
""" figure out if the tunnel is recently active or not """
- status = "inactive"
- if (wgpeer['latest_handshake'] is None):
+ status = 'inactive'
+ if wgpeer['latest_handshake'] is None:
""" no handshake ever """
- status = "inactive"
+ status = 'inactive'
else:
if int(wgpeer['latest_handshake']) > 0:
- delta = timedelta(seconds=int(
- time.time() - wgpeer['latest_handshake']))
- answer += " latest handshake: {}\n".format(delta)
- if (time.time() - int(wgpeer['latest_handshake']) < (60*5)):
+ delta = timedelta(
+ seconds=int(time.time() - wgpeer['latest_handshake'])
+ )
+ answer += ' latest handshake: {}\n'.format(delta)
+ if time.time() - int(wgpeer['latest_handshake']) < (60 * 5):
""" Five minutes and the tunnel is still active """
- status = "active"
+ status = 'active'
else:
""" it's been longer than 5 minutes """
- status = "inactive"
+ status = 'inactive'
elif int(wgpeer['latest_handshake']) == 0:
""" no handshake ever """
- status = "inactive"
- answer += " status: {}\n".format(status)
+ status = 'inactive'
+ answer += ' status: {}\n'.format(status)
if wgpeer['endpoint'] is not None:
- answer += " endpoint: {}\n".format(wgpeer['endpoint'])
+ answer += ' endpoint: {}\n'.format(wgpeer['endpoint'])
if wgpeer['allowed_ips'] is not None:
- answer += " allowed ips: {}\n".format(
- ",".join(wgpeer['allowed_ips']).replace(",", ", "))
+ answer += ' allowed ips: {}\n'.format(
+ ','.join(wgpeer['allowed_ips']).replace(',', ', ')
+ )
if wgpeer['transfer_rx'] > 0 or wgpeer['transfer_tx'] > 0:
- rx_size = size(
- wgpeer['transfer_rx'], system=alternative)
- tx_size = size(
- wgpeer['transfer_tx'], system=alternative)
- answer += " transfer: {} received, {} sent\n".format(
- rx_size, tx_size)
+ rx_size = size(wgpeer['transfer_rx'], system=alternative)
+ tx_size = size(wgpeer['transfer_tx'], system=alternative)
+ answer += ' transfer: {} received, {} sent\n'.format(
+ rx_size, tx_size
+ )
if wgpeer['persistent_keepalive'] is not None:
- answer += " persistent keepalive: every {} seconds\n".format(
- wgpeer['persistent_keepalive'])
+ answer += ' persistent keepalive: every {} seconds\n'.format(
+ wgpeer['persistent_keepalive']
+ )
answer += '\n'
- return answer + super().formated_stats()
+ return answer
@Interface.register
@@ -151,27 +165,29 @@ class WireGuardIf(Interface):
**Interface.definition,
**{
'section': 'wireguard',
- 'prefixes': ['wg', ],
+ 'prefixes': [
+ 'wg',
+ ],
'bridgeable': False,
- }
+ },
}
def get_mac(self):
- """ Get a synthetic MAC address. """
+ """Get a synthetic MAC address."""
return self.get_mac_synthetic()
def update(self, config):
- """ General helper function which works on a dictionary retrived by
+ """General helper function which works on a dictionary retrived by
get_config_dict(). It's main intention is to consolidate the scattered
interface setup code and provide a single point of entry when workin
- on any interface. """
+ on any interface."""
tmp_file = NamedTemporaryFile('w')
tmp_file.write(config['private_key'])
tmp_file.flush()
# Wireguard base command is identical for every peer
- base_cmd = 'wg set {ifname}'
+ base_cmd = 'wg set {ifname}'
if 'port' in config:
base_cmd += ' listen-port {port}'
if 'fwmark' in config:
@@ -201,7 +217,7 @@ class WireGuardIf(Interface):
cmd += f' preshared-key {psk_file}'
# Persistent keepalive is optional
- if 'persistent_keepalive'in peer_config:
+ if 'persistent_keepalive' in peer_config:
cmd += ' persistent-keepalive {persistent_keepalive}'
# Multiple allowed-ip ranges can be defined - ensure we are always
diff --git a/smoketest/scripts/cli/test_service_ntp.py b/smoketest/scripts/cli/test_service_ntp.py
index ae45fe2f4..07af4f5eb 100755
--- a/smoketest/scripts/cli/test_service_ntp.py
+++ b/smoketest/scripts/cli/test_service_ntp.py
@@ -21,6 +21,7 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
+from vyos.xml_ref import default_value
PROCESS_NAME = 'chronyd'
NTP_CONF = '/run/chrony/chrony.conf'
@@ -165,5 +166,99 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'maxslewrate 1000', config)
self.assertIn(f'smoothtime 400 0.001024 leaponly', config)
+ def test_interleave_option(self):
+ # "interleave" option differs from some others in that the
+ # name is not a 1:1 mapping from VyOS config
+ servers = ['192.0.2.1', '192.0.2.2']
+ options = ['prefer']
+
+ for server in servers:
+ for option in options:
+ self.cli_set(base_path + ['server', server, option])
+ self.cli_set(base_path + ['server', server, 'interleave'])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check generated configuration
+ # this file must be read with higher permissions
+ config = cmd(f'sudo cat {NTP_CONF}')
+ self.assertIn('driftfile /run/chrony/drift', config)
+ self.assertIn('dumpdir /run/chrony', config)
+ self.assertIn('ntsdumpdir /run/chrony', config)
+ self.assertIn('clientloglimit 1048576', config)
+ self.assertIn('rtcsync', config)
+ self.assertIn('makestep 1.0 3', config)
+ self.assertIn('leapsectz right/UTC', config)
+
+ for server in servers:
+ self.assertIn(f'server {server} iburst ' + ' '.join(options) + ' xleave', config)
+
+ def test_offload_timestamp_default(self):
+ # Test offloading of NIC timestamp
+ servers = ['192.0.2.1', '192.0.2.2']
+ ptp_port = '8319'
+
+ for server in servers:
+ self.cli_set(base_path + ['server', server, 'ptp'])
+
+ self.cli_set(base_path + ['ptp', 'port', ptp_port])
+ self.cli_set(base_path + ['ptp', 'timestamp', 'interface', 'all'])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check generated configuration
+ # this file must be read with higher permissions
+ config = cmd(f'sudo cat {NTP_CONF}')
+ self.assertIn('driftfile /run/chrony/drift', config)
+ self.assertIn('dumpdir /run/chrony', config)
+ self.assertIn('ntsdumpdir /run/chrony', config)
+ self.assertIn('clientloglimit 1048576', config)
+ self.assertIn('rtcsync', config)
+ self.assertIn('makestep 1.0 3', config)
+ self.assertIn('leapsectz right/UTC', config)
+
+ for server in servers:
+ self.assertIn(f'server {server} iburst port {ptp_port}', config)
+
+ self.assertIn('hwtimestamp *', config)
+
+ def test_ptp_transport(self):
+ # Test offloading of NIC timestamp
+ servers = ['192.0.2.1', '192.0.2.2']
+ options = ['prefer']
+
+ default_ptp_port = default_value(base_path + ['ptp', 'port'])
+
+ for server in servers:
+ for option in options:
+ self.cli_set(base_path + ['server', server, option])
+ self.cli_set(base_path + ['server', server, 'ptp'])
+
+ # commit changes (expected to fail)
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # add the required top-level option and commit
+ self.cli_set(base_path + ['ptp'])
+ self.cli_commit()
+
+ # Check generated configuration
+ # this file must be read with higher permissions
+ config = cmd(f'sudo cat {NTP_CONF}')
+ self.assertIn('driftfile /run/chrony/drift', config)
+ self.assertIn('dumpdir /run/chrony', config)
+ self.assertIn('ntsdumpdir /run/chrony', config)
+ self.assertIn('clientloglimit 1048576', config)
+ self.assertIn('rtcsync', config)
+ self.assertIn('makestep 1.0 3', config)
+ self.assertIn('leapsectz right/UTC', config)
+
+ for server in servers:
+ self.assertIn(f'server {server} iburst ' + ' '.join(options) + f' port {default_ptp_port}', config)
+
+ self.assertIn(f'ptpport {default_ptp_port}', config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/activation-scripts/20-ethernet_offload.py b/src/activation-scripts/20-ethernet_offload.py
index 33b0ea469..ca7213512 100755
--- a/src/activation-scripts/20-ethernet_offload.py
+++ b/src/activation-scripts/20-ethernet_offload.py
@@ -17,9 +17,12 @@
# CLI. See https://vyos.dev/T3619#102254 for all the details.
# T3787: Remove deprecated UDP fragmentation offloading option
# T6006: add to activation-scripts: migration-scripts/interfaces/20-to-21
+# T6716: Honor the configured offload settings and don't automatically add
+# them to the config if the kernel has them set (unless its a live boot)
from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
+from vyos.system.image import is_live_boot
def activate(config: ConfigTree):
base = ['interfaces', 'ethernet']
@@ -36,7 +39,7 @@ def activate(config: ConfigTree):
enabled, fixed = eth.get_generic_receive_offload()
if configured and fixed:
config.delete(base + [ifname, 'offload', 'gro'])
- elif enabled and not fixed:
+ elif is_live_boot() and enabled and not fixed:
config.set(base + [ifname, 'offload', 'gro'])
# If GSO is enabled by the Kernel - we reflect this on the CLI. If GSO is
@@ -45,7 +48,7 @@ def activate(config: ConfigTree):
enabled, fixed = eth.get_generic_segmentation_offload()
if configured and fixed:
config.delete(base + [ifname, 'offload', 'gso'])
- elif enabled and not fixed:
+ elif is_live_boot() and enabled and not fixed:
config.set(base + [ifname, 'offload', 'gso'])
# If LRO is enabled by the Kernel - we reflect this on the CLI. If LRO is
@@ -54,7 +57,7 @@ def activate(config: ConfigTree):
enabled, fixed = eth.get_large_receive_offload()
if configured and fixed:
config.delete(base + [ifname, 'offload', 'lro'])
- elif enabled and not fixed:
+ elif is_live_boot() and enabled and not fixed:
config.set(base + [ifname, 'offload', 'lro'])
# If SG is enabled by the Kernel - we reflect this on the CLI. If SG is
@@ -63,7 +66,7 @@ def activate(config: ConfigTree):
enabled, fixed = eth.get_scatter_gather()
if configured and fixed:
config.delete(base + [ifname, 'offload', 'sg'])
- elif enabled and not fixed:
+ elif is_live_boot() and enabled and not fixed:
config.set(base + [ifname, 'offload', 'sg'])
# If TSO is enabled by the Kernel - we reflect this on the CLI. If TSO is
@@ -72,7 +75,7 @@ def activate(config: ConfigTree):
enabled, fixed = eth.get_tcp_segmentation_offload()
if configured and fixed:
config.delete(base + [ifname, 'offload', 'tso'])
- elif enabled and not fixed:
+ elif is_live_boot() and enabled and not fixed:
config.set(base + [ifname, 'offload', 'tso'])
# Remove deprecated UDP fragmentation offloading option
diff --git a/src/conf_mode/service_ntp.py b/src/conf_mode/service_ntp.py
index 83880fd72..32563aa0e 100755
--- a/src/conf_mode/service_ntp.py
+++ b/src/conf_mode/service_ntp.py
@@ -17,6 +17,7 @@
import os
from vyos.config import Config
+from vyos.config import config_dict_merge
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
from vyos.configverify import verify_interface_exists
@@ -42,13 +43,21 @@ def get_config(config=None):
if not conf.exists(base):
return None
- ntp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, with_defaults=True)
+ ntp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
ntp['config_file'] = config_file
ntp['user'] = user_group
tmp = is_node_changed(conf, base + ['vrf'])
if tmp: ntp.update({'restart_required': {}})
+ # 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 = conf.get_config_defaults(**ntp.kwargs, recursive=True)
+ # Only defined PTP default port, if PTP feature is in use
+ if 'ptp' not in ntp:
+ del default_values['ptp']
+
+ ntp = config_dict_merge(default_values, ntp)
return ntp
def verify(ntp):
@@ -87,6 +96,15 @@ def verify(ntp):
if ipv6_addresses > 1:
raise ConfigError(f'NTP Only admits one ipv6 value for listen-address parameter ')
+ if 'server' in ntp:
+ for host, server in ntp['server'].items():
+ if 'ptp' in server:
+ if 'ptp' not in ntp:
+ raise ConfigError('PTP must be enabled for the NTP service '\
+ f'before it can be used for server "{host}"')
+ else:
+ break
+
return None
def generate(ntp):
diff --git a/src/conf_mode/system_syslog.py b/src/conf_mode/system_syslog.py
index 07fbb0734..2497c5bb6 100755
--- a/src/conf_mode/system_syslog.py
+++ b/src/conf_mode/system_syslog.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-2024 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,6 +18,7 @@ import os
from sys import exit
+from vyos.base import Warning
from vyos.config import Config
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
@@ -58,6 +59,12 @@ def verify(syslog):
if not syslog:
return None
+ if 'host' in syslog:
+ for host, host_options in syslog['host'].items():
+ if 'protocol' in host_options and host_options['protocol'] == 'udp':
+ if 'format' in host_options and 'octet_counted' in host_options['format']:
+ Warning(f'Syslog UDP transport for "{host}" should not use octet-counted format!')
+
verify_vrf(syslog)
def generate(syslog):
diff --git a/src/op_mode/interfaces_wireguard.py b/src/op_mode/interfaces_wireguard.py
new file mode 100644
index 000000000..627af0579
--- /dev/null
+++ b/src/op_mode/interfaces_wireguard.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 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.opmode
+
+from vyos.ifconfig import WireGuardIf
+from vyos.configquery import ConfigTreeQuery
+
+
+def _verify(func):
+ """Decorator checks if WireGuard interface config exists"""
+ from functools import wraps
+
+ @wraps(func)
+ def _wrapper(*args, **kwargs):
+ config = ConfigTreeQuery()
+ interface = kwargs.get('intf_name')
+ if not config.exists(['interfaces', 'wireguard', interface]):
+ unconf_message = f'WireGuard interface {interface} is not configured'
+ raise vyos.opmode.UnconfiguredSubsystem(unconf_message)
+ return func(*args, **kwargs)
+
+ return _wrapper
+
+
+@_verify
+def show_summary(raw: bool, intf_name: str):
+ intf = WireGuardIf(intf_name, create=False, debug=False)
+ return intf.operational.show_interface()
+
+
+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/services/vyos-configd b/src/services/vyos-configd
index 3674d9627..2c0244a81 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# pylint: disable=redefined-outer-name
+
import os
import sys
import grp
@@ -23,8 +25,10 @@ import typing
import logging
import signal
import importlib.util
+import io
+from contextlib import redirect_stdout
+
import zmq
-from contextlib import contextmanager
from vyos.defaults import directories
from vyos.utils.boot import boot_configuration_complete
@@ -49,7 +53,8 @@ if debug:
else:
logger.setLevel(logging.INFO)
-SOCKET_PATH = "ipc:///run/vyos-configd.sock"
+SOCKET_PATH = 'ipc:///run/vyos-configd.sock'
+MAX_MSG_SIZE = 65535
# Response error codes
R_SUCCESS = 1
@@ -64,9 +69,6 @@ configd_env_unset_file = os.path.join(directories['data'], 'vyos-configd-env-uns
# sourced on entering config session
configd_env_file = '/etc/default/vyos-configd-env'
-session_out = None
-session_mode = None
-
def key_name_from_file_name(f):
return os.path.splitext(f)[0]
@@ -76,17 +78,19 @@ def module_name_from_key(k):
def path_from_file_name(f):
return os.path.join(vyos_conf_scripts_dir, f)
+
# opt-in to be run by daemon
with open(configd_include_file) as f:
try:
include = json.load(f)
except OSError as e:
- logger.critical(f"configd include file error: {e}")
+ logger.critical(f'configd include file error: {e}')
sys.exit(1)
except json.JSONDecodeError as e:
- logger.critical(f"JSON load error: {e}")
+ logger.critical(f'JSON load error: {e}')
sys.exit(1)
+
# import conf_mode scripts
(_, _, filenames) = next(iter(os.walk(vyos_conf_scripts_dir)))
filenames.sort()
@@ -110,31 +114,17 @@ conf_mode_scripts = dict(zip(imports, modules))
exclude_set = {key_name_from_file_name(f) for f in filenames if f not in include}
include_set = {key_name_from_file_name(f) for f in filenames if f in include}
-@contextmanager
-def stdout_redirected(filename, mode):
- saved_stdout_fd = None
- destination_file = None
- try:
- sys.stdout.flush()
- saved_stdout_fd = os.dup(sys.stdout.fileno())
- destination_file = open(filename, mode)
- os.dup2(destination_file.fileno(), sys.stdout.fileno())
- yield
- finally:
- if saved_stdout_fd is not None:
- os.dup2(saved_stdout_fd, sys.stdout.fileno())
- os.close(saved_stdout_fd)
- if destination_file is not None:
- destination_file.close()
-
-def explicit_print(path, mode, msg):
- try:
- with open(path, mode) as f:
- f.write(f"\n{msg}\n\n")
- except OSError:
- logger.critical("error explicit_print")
-def run_script(script_name, config, args) -> int:
+def write_stdout_log(file_name, msg):
+ if boot_configuration_complete():
+ return
+ with open(file_name, 'a') as f:
+ f.write(msg)
+
+
+def run_script(script_name, config, args) -> tuple[int, str]:
+ # pylint: disable=broad-exception-caught
+
script = conf_mode_scripts[script_name]
script.argv = args
config.set_level([])
@@ -145,64 +135,53 @@ def run_script(script_name, config, args) -> int:
script.apply(c)
except ConfigError as e:
logger.error(e)
- explicit_print(session_out, session_mode, str(e))
- return R_ERROR_COMMIT
+ return R_ERROR_COMMIT, str(e)
except Exception as e:
logger.critical(e)
- return R_ERROR_DAEMON
+ return R_ERROR_DAEMON, str(e)
+
+ return R_SUCCESS, ''
- return R_SUCCESS
def initialization(socket):
- global session_out
- global session_mode
+ # pylint: disable=broad-exception-caught,too-many-locals
+
# Reset config strings:
active_string = ''
session_string = ''
# check first for resent init msg, in case of client timeout
while True:
- msg = socket.recv().decode("utf-8", "ignore")
+ msg = socket.recv().decode('utf-8', 'ignore')
try:
message = json.loads(msg)
- if message["type"] == "init":
- resp = "init"
+ if message['type'] == 'init':
+ resp = 'init'
socket.send(resp.encode())
- except:
+ except Exception:
break
# zmq synchronous for ipc from single client:
active_string = msg
- resp = "active"
+ resp = 'active'
socket.send(resp.encode())
- session_string = socket.recv().decode("utf-8", "ignore")
- resp = "session"
+ session_string = socket.recv().decode('utf-8', 'ignore')
+ resp = 'session'
socket.send(resp.encode())
- pid_string = socket.recv().decode("utf-8", "ignore")
- resp = "pid"
+ pid_string = socket.recv().decode('utf-8', 'ignore')
+ resp = 'pid'
socket.send(resp.encode())
- sudo_user_string = socket.recv().decode("utf-8", "ignore")
- resp = "sudo_user"
+ sudo_user_string = socket.recv().decode('utf-8', 'ignore')
+ resp = 'sudo_user'
socket.send(resp.encode())
- temp_config_dir_string = socket.recv().decode("utf-8", "ignore")
- resp = "temp_config_dir"
+ temp_config_dir_string = socket.recv().decode('utf-8', 'ignore')
+ resp = 'temp_config_dir'
socket.send(resp.encode())
- changes_only_dir_string = socket.recv().decode("utf-8", "ignore")
- resp = "changes_only_dir"
+ changes_only_dir_string = socket.recv().decode('utf-8', 'ignore')
+ resp = 'changes_only_dir'
socket.send(resp.encode())
- logger.debug(f"config session pid is {pid_string}")
- logger.debug(f"config session sudo_user is {sudo_user_string}")
-
- try:
- session_out = os.readlink(f"/proc/{pid_string}/fd/1")
- session_mode = 'w'
- except FileNotFoundError:
- session_out = None
-
- # if not a 'live' session, for example on boot, write to file
- if not session_out or not boot_configuration_complete():
- session_out = script_stdout_log
- session_mode = 'a'
+ logger.debug(f'config session pid is {pid_string}')
+ logger.debug(f'config session sudo_user is {sudo_user_string}')
os.environ['SUDO_USER'] = sudo_user_string
if temp_config_dir_string:
@@ -229,10 +208,12 @@ def initialization(socket):
return config
-def process_node_data(config, data, last: bool = False) -> int:
+
+def process_node_data(config, data, _last: bool = False) -> tuple[int, str]:
if not config:
- logger.critical(f"Empty config")
- return R_ERROR_DAEMON
+ out = 'Empty config'
+ logger.critical(out)
+ return R_ERROR_DAEMON, out
script_name = None
os.environ['VYOS_TAGNODE_VALUE'] = ''
@@ -246,8 +227,9 @@ def process_node_data(config, data, last: bool = False) -> int:
if res.group(2):
script_name = res.group(2)
if not script_name:
- logger.critical(f"Missing script_name")
- return R_ERROR_DAEMON
+ out = 'Missing script_name'
+ logger.critical(out)
+ return R_ERROR_DAEMON, out
if res.group(3):
args = res.group(3).split()
args.insert(0, f'{script_name}.py')
@@ -259,26 +241,55 @@ def process_node_data(config, data, last: bool = False) -> int:
scripts_called.append(script_record)
if script_name not in include_set:
- return R_PASS
+ return R_PASS, ''
+
+ with redirect_stdout(io.StringIO()) as o:
+ result, err_out = run_script(script_name, config, args)
+ amb_out = o.getvalue()
+ o.close()
+
+ out = amb_out + err_out
+
+ return result, out
+
- with stdout_redirected(session_out, session_mode):
- result = run_script(script_name, config, args)
+def send_result(sock, err, msg):
+ msg_size = min(MAX_MSG_SIZE, len(msg)) if msg else 0
+
+ err_rep = err.to_bytes(1, byteorder=sys.byteorder)
+ logger.debug(f'Sending reply: {err}')
+ sock.send(err_rep)
+
+ # size req from vyshim client
+ size_req = sock.recv().decode()
+ logger.debug(f'Received request: {size_req}')
+ msg_size_rep = hex(msg_size).encode()
+ sock.send(msg_size_rep)
+ logger.debug(f'Sending reply: {msg_size}')
+
+ if msg_size > 0:
+ # send req is sent from vyshim client only if msg_size > 0
+ send_req = sock.recv().decode()
+ logger.debug(f'Received request: {send_req}')
+ sock.send(msg.encode())
+ logger.debug('Sending reply with output')
+
+ write_stdout_log(script_stdout_log, msg)
- return result
def remove_if_file(f: str):
try:
os.remove(f)
except FileNotFoundError:
pass
- except OSError:
- raise
+
def shutdown():
remove_if_file(configd_env_file)
os.symlink(configd_env_unset_file, configd_env_file)
sys.exit(0)
+
if __name__ == '__main__':
context = zmq.Context()
socket = context.socket(zmq.REP)
@@ -294,6 +305,7 @@ if __name__ == '__main__':
os.environ['VYOS_CONFIGD'] = 't'
def sig_handler(signum, frame):
+ # pylint: disable=unused-argument
shutdown()
signal.signal(signal.SIGTERM, sig_handler)
@@ -308,20 +320,19 @@ if __name__ == '__main__':
while True:
# Wait for next request from client
msg = socket.recv().decode()
- logger.debug(f"Received message: {msg}")
+ logger.debug(f'Received message: {msg}')
message = json.loads(msg)
- if message["type"] == "init":
- resp = "init"
+ if message['type'] == 'init':
+ resp = 'init'
socket.send(resp.encode())
config = initialization(socket)
- elif message["type"] == "node":
- res = process_node_data(config, message["data"], message["last"])
- response = res.to_bytes(1, byteorder=sys.byteorder)
- logger.debug(f"Sending response {res}")
- socket.send(response)
- if message["last"] and config:
+ elif message['type'] == 'node':
+ res, out = process_node_data(config, message['data'], message['last'])
+ send_result(socket, res, out)
+
+ if message['last'] and config:
scripts_called = getattr(config, 'scripts_called', [])
logger.debug(f'scripts_called: {scripts_called}')
else:
- logger.critical(f"Unexpected message: {message}")
+ logger.critical(f'Unexpected message: {message}')
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index 97633577d..91100410c 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -577,7 +577,9 @@ def _configure_op(data: Union[ConfigureModel, ConfigureListModel,
background_tasks.add_task(call_commit, session)
msg = self_ref_msg
else:
- session.commit()
+ # capture non-fatal warnings
+ out = session.commit()
+ msg = out if out else msg
logger.info(f"Configuration modified via HTTP API using key '{app.state.vyos_id}'")
except ConfigSessionError as e:
diff --git a/src/shim/vyshim.c b/src/shim/vyshim.c
index a78f62a7b..68e6c4015 100644
--- a/src/shim/vyshim.c
+++ b/src/shim/vyshim.c
@@ -67,6 +67,8 @@ void timer_handler(int);
double get_posix_clock_time(void);
+static char * s_recv_string (void *, int);
+
int main(int argc, char* argv[])
{
// string for node data: conf_mode script and tagnode, if applicable
@@ -119,31 +121,44 @@ int main(int argc, char* argv[])
zmq_recv(requester, error_code, 1, 0);
debug_print("Received node data receipt\n");
- int err = (int)error_code[0];
+ char msg_size_str[7];
+ zmq_send(requester, "msg_size", 8, 0);
+ zmq_recv(requester, msg_size_str, 6, 0);
+ msg_size_str[6] = '\0';
+ int msg_size = (int)strtol(msg_size_str, NULL, 16);
+ debug_print("msg_size: %d\n", msg_size);
+
+ if (msg_size > 0) {
+ zmq_send(requester, "send", 4, 0);
+ char *msg = s_recv_string(requester, msg_size);
+ printf("%s", msg);
+ free(msg);
+ }
free(string_node_data_msg);
- zmq_close(requester);
- zmq_ctx_destroy(context);
+ int err = (int)error_code[0];
+ int ret = 0;
if (err & PASS) {
debug_print("Received PASS\n");
- int ret = pass_through(argv, ex_index);
- return ret;
+ ret = pass_through(argv, ex_index);
}
if (err & ERROR_DAEMON) {
debug_print("Received ERROR_DAEMON\n");
- int ret = pass_through(argv, ex_index);
- return ret;
+ ret = pass_through(argv, ex_index);
}
if (err & ERROR_COMMIT) {
debug_print("Received ERROR_COMMIT\n");
- return -1;
+ ret = -1;
}
- return 0;
+ zmq_close(requester);
+ zmq_ctx_destroy(context);
+
+ return ret;
}
int initialization(void* Requester)
@@ -342,3 +357,15 @@ double get_posix_clock_time(void)
double get_posix_clock_time(void)
{return (double)0;}
#endif
+
+// Receive string from socket and convert into C string
+static char * s_recv_string (void *socket, int bufsize) {
+ char * buffer = (char *)malloc(bufsize+1);
+ int size = zmq_recv(socket, buffer, bufsize, 0);
+ if (size == -1)
+ return NULL;
+ if (size > bufsize)
+ size = bufsize;
+ buffer[size] = '\0';
+ return buffer;
+}