summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/trigger-pr-mirror-repo-sync.yml5
-rw-r--r--data/templates/dhcp-client/ipv6.override.conf.j23
-rw-r--r--data/templates/dhcp-server/kea-ctrl-agent.conf.j214
-rw-r--r--data/templates/dhcp-server/kea-dhcp4.conf.j224
-rw-r--r--data/templates/ids/fastnetmon.j2121
-rw-r--r--data/templates/ids/fastnetmon_excluded_networks_list.j25
-rw-r--r--data/templates/ids/fastnetmon_networks_list.j25
-rw-r--r--data/templates/ipsec/swanctl/peer.j215
-rw-r--r--debian/control1
-rw-r--r--debian/vyos-1x.postinst2
-rw-r--r--interface-definitions/include/dhcp/ping-check.xml.i8
-rw-r--r--interface-definitions/include/version/ids-version.xml.i2
-rw-r--r--interface-definitions/service_dhcp-server.xml.in2
-rw-r--r--interface-definitions/service_ids_ddos-protection.xml.in167
-rw-r--r--interface-definitions/vpn_ipsec.xml.in57
m---------libvyosconfig0
-rw-r--r--op-mode-definitions/monitor-log.xml.in13
-rwxr-xr-xop-mode-definitions/show-log.xml.in48
-rw-r--r--op-mode-definitions/system-image.xml.in2
-rw-r--r--python/vyos/component_version.py63
-rw-r--r--python/vyos/configsession.py55
-rw-r--r--python/vyos/kea.py13
-rw-r--r--python/vyos/proto/vyconf_client.py4
-rw-r--r--python/vyos/system/grub_util.py5
-rwxr-xr-xpython/vyos/template.py28
-rw-r--r--python/vyos/vyconf_session.py123
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-server.py61
-rwxr-xr-xsmoketest/scripts/cli/test_service_ids_ddos-protection.py116
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py88
-rwxr-xr-xsrc/conf_mode/service_dhcp-server.py13
-rwxr-xr-xsrc/conf_mode/service_ids_ddos-protection.py104
-rwxr-xr-xsrc/conf_mode/system_option.py7
-rwxr-xr-xsrc/etc/netplug/vyos-netplug-dhcp-client39
-rw-r--r--src/etc/systemd/system/fastnetmon.service.d/override.conf12
-rw-r--r--src/etc/systemd/system/kea-ctrl-agent.service.d/override.conf10
-rwxr-xr-xsrc/init/vyos-router5
-rw-r--r--src/migration-scripts/dhcp-server/7-to-86
-rw-r--r--src/migration-scripts/ids/1-to-230
-rwxr-xr-xsrc/op_mode/image_installer.py44
-rw-r--r--src/systemd/netplug.service9
-rw-r--r--src/systemd/vyos.target2
41 files changed, 608 insertions, 723 deletions
diff --git a/.github/workflows/trigger-pr-mirror-repo-sync.yml b/.github/workflows/trigger-pr-mirror-repo-sync.yml
index f74895987..978be0582 100644
--- a/.github/workflows/trigger-pr-mirror-repo-sync.yml
+++ b/.github/workflows/trigger-pr-mirror-repo-sync.yml
@@ -6,6 +6,11 @@ on:
branches:
- current
+permissions:
+ pull-requests: write
+ contents: write
+ issues: write
+
jobs:
call-trigger-mirror-pr-repo-sync:
if: github.repository_owner == 'vyos' && github.event.pull_request.merged == true
diff --git a/data/templates/dhcp-client/ipv6.override.conf.j2 b/data/templates/dhcp-client/ipv6.override.conf.j2
index b0c0e0544..d270a55fc 100644
--- a/data/templates/dhcp-client/ipv6.override.conf.j2
+++ b/data/templates/dhcp-client/ipv6.override.conf.j2
@@ -4,6 +4,9 @@
[Unit]
ConditionPathExists={{ dhcp6_client_dir }}/dhcp6c.%i.conf
+{% if ifname.startswith('pppoe') %}
+After=ppp@{{ ifname }}.service
+{% endif %}
[Service]
ExecStart=
diff --git a/data/templates/dhcp-server/kea-ctrl-agent.conf.j2 b/data/templates/dhcp-server/kea-ctrl-agent.conf.j2
deleted file mode 100644
index b37cf4798..000000000
--- a/data/templates/dhcp-server/kea-ctrl-agent.conf.j2
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "Control-agent": {
-{% if high_availability is vyos_defined %}
- "http-host": "{{ high_availability.source_address }}",
- "http-port": 647,
- "control-sockets": {
- "dhcp4": {
- "socket-type": "unix",
- "socket-name": "/run/kea/dhcp4-ctrl-socket"
- }
- }
-{% endif %}
- }
-}
diff --git a/data/templates/dhcp-server/kea-dhcp4.conf.j2 b/data/templates/dhcp-server/kea-dhcp4.conf.j2
index 2e10d58e0..8d9ffb194 100644
--- a/data/templates/dhcp-server/kea-dhcp4.conf.j2
+++ b/data/templates/dhcp-server/kea-dhcp4.conf.j2
@@ -25,20 +25,6 @@
},
"option-def": [
{
- "name": "rfc3442-static-route",
- "code": 121,
- "type": "record",
- "array": true,
- "record-types": "uint8,uint8,uint8,uint8,uint8,uint8,uint8"
- },
- {
- "name": "windows-static-route",
- "code": 249,
- "type": "record",
- "array": true,
- "record-types": "uint8,uint8,uint8,uint8,uint8,uint8,uint8"
- },
- {
"name": "wpad-url",
"code": 252,
"type": "string"
@@ -69,6 +55,16 @@
},
{% endif %}
{
+ "library": "/usr/lib/{{ machine }}-linux-gnu/kea/hooks/libdhcp_ping_check.so",
+ "parameters": {
+ "enable-ping-check" : false,
+ "min-ping-requests" : 1,
+ "reply-timeout" : 100,
+ "ping-cltt-secs" : 60,
+ "ping-channel-threads" : 0
+ }
+ },
+ {
"library": "/usr/lib/{{ machine }}-linux-gnu/kea/hooks/libdhcp_lease_cmds.so",
"parameters": {}
}
diff --git a/data/templates/ids/fastnetmon.j2 b/data/templates/ids/fastnetmon.j2
deleted file mode 100644
index f6f03d0db..000000000
--- a/data/templates/ids/fastnetmon.j2
+++ /dev/null
@@ -1,121 +0,0 @@
-# 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 = /run/fastnetmon/networks_list
-
-# list networks in CIDR format which will be not monitored for attacks
-white_list_path = /run/fastnetmon/excluded_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
-{% 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
-unban_only_if_attack_finished = on
-
-# enable per subnet speed meters
-# For each subnet, list track speed in bps and pps for both directions
-enable_subnet_counters = off
-
-{% if mode is vyos_defined('mirror') %}
-mirror_afpacket = on
-{% elif mode is vyos_defined('sflow') %}
-sflow = on
-{% if sflow.port is vyos_defined %}
-sflow_port = {{ sflow.port }}
-{% endif %}
-{% if sflow.listen_address is vyos_defined %}
-sflow_host = {{ sflow.listen_address }}
-{% endif %}
-{% endif %}
-
-
-process_incoming_traffic = {{ 'on' if direction is vyos_defined and 'in' in direction else 'off' }}
-process_outgoing_traffic = {{ 'on' if direction is vyos_defined and 'out' in direction else 'off' }}
-
-{% if threshold is vyos_defined %}
-{% if threshold.general is vyos_defined %}
-# General threshold
-{% for thr, thr_value in threshold.general.items() %}
-{% if thr is vyos_defined('fps') %}
-ban_for_flows = on
-threshold_flows = {{ thr_value }}
-{% elif thr is vyos_defined('mbps') %}
-ban_for_bandwidth = on
-threshold_mbps = {{ thr_value }}
-{% elif thr is vyos_defined('pps') %}
-ban_for_pps = on
-threshold_pps = {{ thr_value }}
-{% endif %}
-{% endfor %}
-{% endif %}
-
-{% if threshold.tcp is vyos_defined %}
-# TCP threshold
-{% for thr, thr_value in threshold.tcp.items() %}
-{% if thr is vyos_defined('fps') %}
-ban_for_tcp_flows = on
-threshold_tcp_flows = {{ thr_value }}
-{% elif thr is vyos_defined('mbps') %}
-ban_for_tcp_bandwidth = on
-threshold_tcp_mbps = {{ thr_value }}
-{% elif thr is vyos_defined('pps') %}
-ban_for_tcp_pps = on
-threshold_tcp_pps = {{ thr_value }}
-{% endif %}
-{% endfor %}
-{% endif %}
-
-{% if threshold.udp is vyos_defined %}
-# UDP threshold
-{% for thr, thr_value in threshold.udp.items() %}
-{% if thr is vyos_defined('fps') %}
-ban_for_udp_flows = on
-threshold_udp_flows = {{ thr_value }}
-{% elif thr is vyos_defined('mbps') %}
-ban_for_udp_bandwidth = on
-threshold_udp_mbps = {{ thr_value }}
-{% elif thr is vyos_defined('pps') %}
-ban_for_udp_pps = on
-threshold_udp_pps = {{ thr_value }}
-{% endif %}
-{% endfor %}
-{% endif %}
-
-{% if threshold.icmp is vyos_defined %}
-# ICMP threshold
-{% for thr, thr_value in threshold.icmp.items() %}
-{% if thr is vyos_defined('fps') %}
-ban_for_icmp_flows = on
-threshold_icmp_flows = {{ thr_value }}
-{% elif thr is vyos_defined('mbps') %}
-ban_for_icmp_bandwidth = on
-threshold_icmp_mbps = {{ thr_value }}
-{% elif thr is vyos_defined('pps') %}
-ban_for_icmp_pps = on
-threshold_icmp_pps = {{ thr_value }}
-{% endif %}
-{% endfor %}
-{% endif %}
-
-{% endif %}
-
-{% if listen_interface is vyos_defined %}
-interfaces = {{ listen_interface | join(',') }}
-{% endif %}
-
-{% if alert_script is vyos_defined %}
-notify_script_path = {{ alert_script }}
-{% endif %}
diff --git a/data/templates/ids/fastnetmon_excluded_networks_list.j2 b/data/templates/ids/fastnetmon_excluded_networks_list.j2
deleted file mode 100644
index c88a1c527..000000000
--- a/data/templates/ids/fastnetmon_excluded_networks_list.j2
+++ /dev/null
@@ -1,5 +0,0 @@
-{% if excluded_network is vyos_defined %}
-{% for net in excluded_network %}
-{{ net }}
-{% endfor %}
-{% endif %}
diff --git a/data/templates/ids/fastnetmon_networks_list.j2 b/data/templates/ids/fastnetmon_networks_list.j2
deleted file mode 100644
index 0a0576d2a..000000000
--- a/data/templates/ids/fastnetmon_networks_list.j2
+++ /dev/null
@@ -1,5 +0,0 @@
-{% if network is vyos_defined %}
-{% for net in network %}
-{{ net }}
-{% endfor %}
-{% endif %}
diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2
index 3a9af2c94..cf0865c88 100644
--- a/data/templates/ipsec/swanctl/peer.j2
+++ b/data/templates/ipsec/swanctl/peer.j2
@@ -68,8 +68,19 @@
rekey_packets = 0
rekey_time = 0s
{% endif %}
- local_ts = 0.0.0.0/0,::/0
- remote_ts = 0.0.0.0/0,::/0
+{# set default traffic-selectors #}
+{% set local_ts = '0.0.0.0/0,::/0' %}
+{% set remote_ts = '0.0.0.0/0,::/0' %}
+{% if peer_conf.vti.traffic_selector is vyos_defined %}
+{% if peer_conf.vti.traffic_selector.local is vyos_defined and peer_conf.vti.traffic_selector.local.prefix is vyos_defined %}
+{% set local_ts = peer_conf.vti.traffic_selector.local.prefix | join(',') %}
+{% endif %}
+{% if peer_conf.vti.traffic_selector.remote is vyos_defined and peer_conf.vti.traffic_selector.remote.prefix is vyos_defined %}
+{% set remote_ts = peer_conf.vti.traffic_selector.remote.prefix | join(',') %}
+{% endif %}
+{% endif %}
+ local_ts = {{ local_ts }}
+ remote_ts = {{ remote_ts }}
updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }}"
{# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #}
{# Thus we simply shift the key by one to also support a vti0 interface #}
diff --git a/debian/control b/debian/control
index c40b0fb04..ffa21f840 100644
--- a/debian/control
+++ b/debian/control
@@ -196,7 +196,6 @@ Depends:
ddclient (>= 3.11.1),
# End "service dns dynamic"
# # For "service ids"
- fastnetmon [amd64],
suricata,
suricata-update,
# End "service ids"
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index fde58651a..798ecaa1b 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -221,11 +221,9 @@ fi
# Remove unwanted daemon files from /etc
# conntackd
# pmacct
-# fastnetmon
# ntp
DELETE="/etc/logrotate.d/conntrackd.distrib /etc/init.d/conntrackd /etc/default/conntrackd
/etc/default/pmacctd /etc/pmacct
- /etc/networks_list /etc/networks_whitelist /etc/fastnetmon.conf
/etc/ntp.conf /etc/default/ssh /etc/avahi/avahi-daemon.conf /etc/avahi/hosts
/etc/powerdns /etc/default/pdns-recursor
/etc/ppp/ip-up.d/0000usepeerdns /etc/ppp/ip-down.d/0000usepeerdns"
diff --git a/interface-definitions/include/dhcp/ping-check.xml.i b/interface-definitions/include/dhcp/ping-check.xml.i
new file mode 100644
index 000000000..a506f68e4
--- /dev/null
+++ b/interface-definitions/include/dhcp/ping-check.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from dhcp/ping-check.xml.i -->
+<leafNode name="ping-check">
+ <properties>
+ <help>Sends ICMP Echo request to the address being assigned</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/version/ids-version.xml.i b/interface-definitions/include/version/ids-version.xml.i
index 9133be02b..6d4e92c21 100644
--- a/interface-definitions/include/version/ids-version.xml.i
+++ b/interface-definitions/include/version/ids-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/ids-version.xml.i -->
-<syntaxVersion component='ids' version='1'></syntaxVersion>
+<syntaxVersion component='ids' version='2'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/service_dhcp-server.xml.in b/interface-definitions/service_dhcp-server.xml.in
index 9a194de4f..c0ab7c048 100644
--- a/interface-definitions/service_dhcp-server.xml.in
+++ b/interface-definitions/service_dhcp-server.xml.in
@@ -112,6 +112,7 @@
</properties>
</leafNode>
#include <include/dhcp/option-v4.xml.i>
+ #include <include/dhcp/ping-check.xml.i>
#include <include/generic-description.xml.i>
#include <include/generic-disable-node.xml.i>
<tagNode name="subnet">
@@ -128,6 +129,7 @@
</properties>
<children>
#include <include/dhcp/option-v4.xml.i>
+ #include <include/dhcp/ping-check.xml.i>
#include <include/generic-description.xml.i>
#include <include/generic-disable-node.xml.i>
<leafNode name="exclude">
diff --git a/interface-definitions/service_ids_ddos-protection.xml.in b/interface-definitions/service_ids_ddos-protection.xml.in
deleted file mode 100644
index 3ef2640b3..000000000
--- a/interface-definitions/service_ids_ddos-protection.xml.in
+++ /dev/null
@@ -1,167 +0,0 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="service">
- <children>
- <node name="ids">
- <properties>
- <help>Intrusion Detection System</help>
- </properties>
- <children>
- <node name="ddos-protection" owner="${vyos_conf_scripts_dir}/service_ids_ddos-protection.py">
- <properties>
- <help>FastNetMon detection and protection parameters</help>
- <priority>731</priority>
- </properties>
- <children>
- <leafNode name="alert-script">
- <properties>
- <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>
- <completionHelp>
- <list>in out</list>
- </completionHelp>
- <constraint>
- <regex>(in|out)</regex>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="excluded-network">
- <properties>
- <help>Specify IPv4 and IPv6 networks which are going to be excluded from protection</help>
- <valueHelp>
- <format>ipv4net</format>
- <description>IPv4 prefix(es) to exclude</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6net</format>
- <description>IPv6 prefix(es) to exclude</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-prefix"/>
- <validator name="ipv6-prefix"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="listen-interface">
- <properties>
- <help>Listen interface for mirroring traffic</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces</script>
- </completionHelp>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="mode">
- <properties>
- <help>Traffic capture mode</help>
- <completionHelp>
- <list>mirror sflow</list>
- </completionHelp>
- <valueHelp>
- <format>mirror</format>
- <description>Listen to mirrored traffic</description>
- </valueHelp>
- <valueHelp>
- <format>sflow</format>
- <description>Capture sFlow flows</description>
- </valueHelp>
- <constraint>
- <regex>(mirror|sflow)</regex>
- </constraint>
- </properties>
- </leafNode>
- <node name="sflow">
- <properties>
- <help>Sflow settings</help>
- </properties>
- <children>
- #include <include/listen-address-ipv4-single.xml.i>
- #include <include/port-number.xml.i>
- <leafNode name="port">
- <defaultValue>6343</defaultValue>
- </leafNode>
- </children>
- </node>
- <leafNode name="network">
- <properties>
- <help>Specify IPv4 and IPv6 networks which belong to you</help>
- <valueHelp>
- <format>ipv4net</format>
- <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>
- </leafNode>
- <node name="threshold">
- <properties>
- <help>Attack limits thresholds</help>
- </properties>
- <children>
- <node name="general">
- <properties>
- <help>General threshold</help>
- </properties>
- <children>
- #include <include/ids/threshold.xml.i>
- </children>
- </node>
- <node name="tcp">
- <properties>
- <help>TCP threshold</help>
- </properties>
- <children>
- #include <include/ids/threshold.xml.i>
- </children>
- </node>
- <node name="udp">
- <properties>
- <help>UDP threshold</help>
- </properties>
- <children>
- #include <include/ids/threshold.xml.i>
- </children>
- </node>
- <node name="icmp">
- <properties>
- <help>ICMP threshold</help>
- </properties>
- <children>
- #include <include/ids/threshold.xml.i>
- </children>
- </node>
- </children>
- </node>
- </children>
- </node>
- </children>
- </node>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
index 0cf526fad..873a4f882 100644
--- a/interface-definitions/vpn_ipsec.xml.in
+++ b/interface-definitions/vpn_ipsec.xml.in
@@ -1244,6 +1244,63 @@
<children>
#include <include/ipsec/bind.xml.i>
#include <include/ipsec/esp-group.xml.i>
+ <node name="traffic-selector">
+ <properties>
+ <help>Traffic-selectors parameters</help>
+ </properties>
+ <children>
+ <node name="local">
+ <properties>
+ <help>Local parameters for interesting traffic</help>
+ </properties>
+ <children>
+ <leafNode name="prefix">
+ <properties>
+ <help>Local IPv4 or IPv6 prefix</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>Local IPv4 prefix</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>Local IPv6 prefix</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="remote">
+ <properties>
+ <help>Remote parameters for interesting traffic</help>
+ </properties>
+ <children>
+ <leafNode name="prefix">
+ <properties>
+ <help>Remote IPv4 or IPv6 prefix</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>Remote IPv4 prefix</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>Remote IPv6 prefix</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/libvyosconfig b/libvyosconfig
-Subproject 58dbb42e827e3d326c6e0e9470334d4d5c7c396
+Subproject 1dedc69476d707718031c45b53b626da8badf86
diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in
index b9ef8f48e..91e1c93ef 100644
--- a/op-mode-definitions/monitor-log.xml.in
+++ b/op-mode-definitions/monitor-log.xml.in
@@ -17,19 +17,6 @@
</properties>
<command>SYSTEMD_COLORS=false grc journalctl --no-hostname --follow --boot</command>
</node>
- <node name="ids">
- <properties>
- <help>Monitor Intrusion Detection System log</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>
<leafNode name="certbot">
<properties>
<help>Monitor last lines of certbot log</help>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index 5ee7c973f..ee2e2bf70 100755
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -50,6 +50,39 @@
</properties>
<command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e heartbeat -e cl_status -e mach_down -e ha_log</command>
</leafNode>
+ <node name="conntrack">
+ <properties>
+ <help>Show log for conntrack events</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -t vyos-conntrack-logger --grep='\[(NEW|UPDATE|DESTROY)\]'</command>
+ <children>
+ <node name="event">
+ <properties>
+ <help>Show log for conntrack events</help>
+ </properties>
+ <children>
+ <leafNode name="new">
+ <properties>
+ <help>Show log for conntrack events</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -t vyos-conntrack-logger --grep='\[(NEW)\]'</command>
+ </leafNode>
+ <leafNode name="update">
+ <properties>
+ <help>Show log for conntrack events</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -t vyos-conntrack-logger --grep='\[(UPDATE)\]'</command>
+ </leafNode>
+ <leafNode name="destroy">
+ <properties>
+ <help>Show log for Conntrack Events</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -t vyos-conntrack-logger --grep='\[(DESTROY)\]'</command>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
<leafNode name="conntrack-sync">
<properties>
<help>Show log for Conntrack-sync</help>
@@ -62,19 +95,6 @@
</properties>
<command>journalctl --no-hostname --boot --unit conserver-server.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>
@@ -139,7 +159,7 @@
<properties>
<help>Show log for Firewall</help>
</properties>
- <command>journalctl --no-hostname --boot -k | egrep "(ipv[46]|bri)-(FWD|INP|OUT|NAM)"</command>
+ <command>journalctl --no-hostname --boot -k --grep='(ipv[46]|bri)-(FWD|INP|OUT|NAM)|STATE-POLICY'</command>
<children>
<node name="bridge">
<properties>
diff --git a/op-mode-definitions/system-image.xml.in b/op-mode-definitions/system-image.xml.in
index 44b055be6..847029dcd 100644
--- a/op-mode-definitions/system-image.xml.in
+++ b/op-mode-definitions/system-image.xml.in
@@ -193,7 +193,7 @@
<properties>
<help>Show installed VyOS images</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/image_info.py show_images_summary</command>
+ <command>${vyos_op_scripts_dir}/image_info.py show_images_summary</command>
<children>
<node name="details">
<properties>
diff --git a/python/vyos/component_version.py b/python/vyos/component_version.py
index 94215531d..81d986658 100644
--- a/python/vyos/component_version.py
+++ b/python/vyos/component_version.py
@@ -49,7 +49,9 @@ DEFAULT_CONFIG_PATH = os.path.join(directories['config'], 'config.boot')
REGEX_WARN_VYOS = r'(// Warning: Do not remove the following line.)'
REGEX_WARN_VYATTA = r'(/\* Warning: Do not remove the following line. \*/)'
REGEX_COMPONENT_VERSION_VYOS = r'// vyos-config-version:\s+"([\w@:-]+)"\s*'
-REGEX_COMPONENT_VERSION_VYATTA = r'/\* === vyatta-config-version:\s+"([\w@:-]+)"\s+=== \*/'
+REGEX_COMPONENT_VERSION_VYATTA = (
+ r'/\* === vyatta-config-version:\s+"([\w@:-]+)"\s+=== \*/'
+)
REGEX_RELEASE_VERSION_VYOS = r'// Release version:\s+(\S*)\s*'
REGEX_RELEASE_VERSION_VYATTA = r'/\* Release version:\s+(\S*)\s*\*/'
@@ -62,16 +64,31 @@ CONFIG_FILE_VERSION = """\
warn_filter_vyos = re.compile(REGEX_WARN_VYOS)
warn_filter_vyatta = re.compile(REGEX_WARN_VYATTA)
-regex_filter = { 'vyos': dict(zip(['component', 'release'],
- [re.compile(REGEX_COMPONENT_VERSION_VYOS),
- re.compile(REGEX_RELEASE_VERSION_VYOS)])),
- 'vyatta': dict(zip(['component', 'release'],
- [re.compile(REGEX_COMPONENT_VERSION_VYATTA),
- re.compile(REGEX_RELEASE_VERSION_VYATTA)])) }
+regex_filter = {
+ 'vyos': dict(
+ zip(
+ ['component', 'release'],
+ [
+ re.compile(REGEX_COMPONENT_VERSION_VYOS),
+ re.compile(REGEX_RELEASE_VERSION_VYOS),
+ ],
+ )
+ ),
+ 'vyatta': dict(
+ zip(
+ ['component', 'release'],
+ [
+ re.compile(REGEX_COMPONENT_VERSION_VYATTA),
+ re.compile(REGEX_RELEASE_VERSION_VYATTA),
+ ],
+ )
+ ),
+}
+
@dataclass
class VersionInfo:
- component: Optional[dict[str,int]] = None
+ component: Optional[dict[str, int]] = None
release: str = get_version()
vintage: str = 'vyos'
config_body: Optional[str] = None
@@ -84,8 +101,9 @@ class VersionInfo:
return bool(self.config_body is None)
def update_footer(self):
- f = CONFIG_FILE_VERSION.format(component_to_string(self.component),
- self.release)
+ f = CONFIG_FILE_VERSION.format(
+ component_to_string(self.component), self.release
+ )
self.footer_lines = f.splitlines()
def update_syntax(self):
@@ -121,13 +139,16 @@ class VersionInfo:
except Exception as e:
raise ValueError(e) from e
+
def component_to_string(component: dict) -> str:
- l = [f'{k}@{v}' for k, v in sorted(component.items(), key=lambda x: x[0])]
+ l = [f'{k}@{v}' for k, v in sorted(component.items(), key=lambda x: x[0])] # noqa: E741
return ':'.join(l)
+
def component_from_string(string: str) -> dict:
return {k: int(v) for k, v in re.findall(r'([\w,-]+)@(\d+)', string)}
+
def version_info_from_file(config_file) -> VersionInfo:
"""Return config file component and release version info."""
version_info = VersionInfo()
@@ -166,27 +187,27 @@ def version_info_from_file(config_file) -> VersionInfo:
return version_info
+
def version_info_from_system() -> VersionInfo:
"""Return system component and release version info."""
d = component_version()
sort_d = dict(sorted(d.items(), key=lambda x: x[0]))
- version_info = VersionInfo(
- component = sort_d,
- release = get_version(),
- vintage = 'vyos'
- )
+ version_info = VersionInfo(component=sort_d, release=get_version(), vintage='vyos')
return version_info
+
def version_info_copy(v: VersionInfo) -> VersionInfo:
"""Make a copy of dataclass."""
return replace(v)
+
def version_info_prune_component(x: VersionInfo, y: VersionInfo) -> VersionInfo:
"""In place pruning of component keys of x not in y."""
if x.component is None or y.component is None:
return
- x.component = { k: v for k,v in x.component.items() if k in y.component }
+ x.component = {k: v for k, v in x.component.items() if k in y.component}
+
def add_system_version(config_str: str = None, out_file: str = None):
"""Wrap config string with system version and write to out_file.
@@ -202,3 +223,11 @@ def add_system_version(config_str: str = None, out_file: str = None):
version_info.write(out_file)
else:
sys.stdout.write(version_info.write_string())
+
+
+def append_system_version(file: str):
+ """Append system version data to existing file"""
+ version_info = version_info_from_system()
+ version_info.update_footer()
+ with open(file, 'a') as f:
+ f.write(version_info.write_string())
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index 90b96b88c..a3be29881 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -21,6 +21,10 @@ import subprocess
from vyos.defaults import directories
from vyos.utils.process import is_systemd_service_running
from vyos.utils.dict import dict_to_paths
+from vyos.utils.boot import boot_configuration_complete
+from vyos.vyconf_session import VyconfSession
+
+vyconf_backend = False
CLI_SHELL_API = '/bin/cli-shell-api'
SET = '/opt/vyatta/sbin/my_set'
@@ -165,6 +169,11 @@ class ConfigSession(object):
self.__run_command([CLI_SHELL_API, 'setupSession'])
+ if vyconf_backend and boot_configuration_complete():
+ self._vyconf_session = VyconfSession(on_error=ConfigSessionError)
+ else:
+ self._vyconf_session = None
+
def __del__(self):
try:
output = (
@@ -209,7 +218,10 @@ class ConfigSession(object):
value = []
else:
value = [value]
- self.__run_command([SET] + path + value)
+ if self._vyconf_session is None:
+ self.__run_command([SET] + path + value)
+ else:
+ self._vyconf_session.set(path + value)
def set_section(self, path: list, d: dict):
try:
@@ -223,7 +235,10 @@ class ConfigSession(object):
value = []
else:
value = [value]
- self.__run_command([DELETE] + path + value)
+ if self._vyconf_session is None:
+ self.__run_command([DELETE] + path + value)
+ else:
+ self._vyconf_session.delete(path + value)
def load_section(self, path: list, d: dict):
try:
@@ -261,20 +276,34 @@ class ConfigSession(object):
self.__run_command([COMMENT] + path + value)
def commit(self):
- out = self.__run_command([COMMIT])
+ if self._vyconf_session is None:
+ out = self.__run_command([COMMIT])
+ else:
+ out, _ = self._vyconf_session.commit()
+
return out
def discard(self):
- self.__run_command([DISCARD])
+ if self._vyconf_session is None:
+ self.__run_command([DISCARD])
+ else:
+ out, _ = self._vyconf_session.discard()
def show_config(self, path, format='raw'):
- config_data = self.__run_command(SHOW_CONFIG + path)
+ if self._vyconf_session is None:
+ config_data = self.__run_command(SHOW_CONFIG + path)
+ else:
+ config_data, _ = self._vyconf_session.show_config()
if format == 'raw':
return config_data
def load_config(self, file_path):
- out = self.__run_command(LOAD_CONFIG + [file_path])
+ if self._vyconf_session is None:
+ out = self.__run_command(LOAD_CONFIG + [file_path])
+ else:
+ out, _ = self._vyconf_session.load_config(file=file_path)
+
return out
def load_explicit(self, file_path):
@@ -287,11 +316,21 @@ class ConfigSession(object):
raise ConfigSessionError(e) from e
def migrate_and_load_config(self, file_path):
- out = self.__run_command(MIGRATE_LOAD_CONFIG + [file_path])
+ if self._vyconf_session is None:
+ out = self.__run_command(MIGRATE_LOAD_CONFIG + [file_path])
+ else:
+ out, _ = self._vyconf_session.load_config(file=file_path, migrate=True)
+
return out
def save_config(self, file_path):
- out = self.__run_command(SAVE_CONFIG + [file_path])
+ if self._vyconf_session is None:
+ out = self.__run_command(SAVE_CONFIG + [file_path])
+ else:
+ out, _ = self._vyconf_session.save_config(
+ file=file_path, append_version=True
+ )
+
return out
def install_image(self, url):
diff --git a/python/vyos/kea.py b/python/vyos/kea.py
index de397d8f9..2b0cac7e6 100644
--- a/python/vyos/kea.py
+++ b/python/vyos/kea.py
@@ -21,7 +21,6 @@ from datetime import datetime
from datetime import timezone
from vyos.template import is_ipv6
-from vyos.template import isc_static_route
from vyos.template import netmask_from_cidr
from vyos.utils.dict import dict_search_args
from vyos.utils.file import file_permissions
@@ -113,22 +112,21 @@ def kea_parse_options(config):
default_route = ''
if 'default_router' in config:
- default_route = isc_static_route('0.0.0.0/0', config['default_router'])
+ default_route = f'0.0.0.0/0 - {config["default_router"]}'
routes = [
- isc_static_route(route, route_options['next_hop'])
+ f'{route} - {route_options["next_hop"]}'
for route, route_options in config['static_route'].items()
]
options.append(
{
- 'name': 'rfc3442-static-route',
+ 'name': 'classless-static-route',
'data': ', '.join(
routes if not default_route else routes + [default_route]
),
}
)
- options.append({'name': 'windows-static-route', 'data': ', '.join(routes)})
if 'time_zone' in config:
with open('/usr/share/zoneinfo/' + config['time_zone'], 'rb') as f:
@@ -149,7 +147,7 @@ def kea_parse_options(config):
def kea_parse_subnet(subnet, config):
- out = {'subnet': subnet, 'id': int(config['subnet_id'])}
+ out = {'subnet': subnet, 'id': int(config['subnet_id']), 'user-context': {}}
if 'option' in config:
out['option-data'] = kea_parse_options(config['option'])
@@ -167,6 +165,9 @@ def kea_parse_subnet(subnet, config):
out['valid-lifetime'] = int(config['lease'])
out['max-valid-lifetime'] = int(config['lease'])
+ if 'ping_check' in config:
+ out['user-context']['enable-ping-check'] = True
+
if 'range' in config:
pools = []
for num, range_config in config['range'].items():
diff --git a/python/vyos/proto/vyconf_client.py b/python/vyos/proto/vyconf_client.py
index f34549309..b385f0951 100644
--- a/python/vyos/proto/vyconf_client.py
+++ b/python/vyos/proto/vyconf_client.py
@@ -52,7 +52,9 @@ def request_to_msg(req: vyconf_proto.RequestEnvelope) -> vyconf_pb2.RequestEnvel
def msg_to_response(msg: vyconf_pb2.Response) -> vyconf_proto.Response:
# pylint: disable=no-member
- d = MessageToDict(msg, preserving_proto_field_name=True)
+ d = MessageToDict(
+ msg, preserving_proto_field_name=True, use_integers_for_enums=True
+ )
response = vyconf_proto.Response(**d)
return response
diff --git a/python/vyos/system/grub_util.py b/python/vyos/system/grub_util.py
index 4a3d8795e..ad95bb4f9 100644
--- a/python/vyos/system/grub_util.py
+++ b/python/vyos/system/grub_util.py
@@ -56,13 +56,12 @@ def set_kernel_cmdline_options(cmdline_options: str, version: str = '',
@image.if_not_live_boot
def update_kernel_cmdline_options(cmdline_options: str,
- root_dir: str = '') -> None:
+ root_dir: str = '',
+ version = image.get_running_image()) -> None:
"""Update Kernel custom cmdline options"""
if not root_dir:
root_dir = disk.find_persistence()
- version = image.get_running_image()
-
boot_opts_current = grub.get_boot_opts(version, root_dir)
boot_opts_proposed = grub.BOOT_OPTS_STEM + f'{version} {cmdline_options}'
diff --git a/python/vyos/template.py b/python/vyos/template.py
index e75db1a8d..7ba85a046 100755
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -390,28 +390,6 @@ def compare_netmask(netmask1, netmask2):
except:
return False
-@register_filter('isc_static_route')
-def isc_static_route(subnet, router):
- # https://ercpe.de/blog/pushing-static-routes-with-isc-dhcp-server
- # Option format is:
- # <netmask>, <network-byte1>, <network-byte2>, <network-byte3>, <router-byte1>, <router-byte2>, <router-byte3>
- # where bytes with the value 0 are omitted.
- from ipaddress import ip_network
- net = ip_network(subnet)
- # add netmask
- string = str(net.prefixlen) + ','
- # add network bytes
- if net.prefixlen:
- width = net.prefixlen // 8
- if net.prefixlen % 8:
- width += 1
- string += ','.join(map(str,tuple(net.network_address.packed)[:width])) + ','
-
- # add router bytes
- string += ','.join(router.split('.'))
-
- return string
-
@register_filter('is_file')
def is_file(filename):
if os.path.exists(filename):
@@ -895,7 +873,8 @@ def kea_shared_network_json(shared_networks):
network = {
'name': name,
'authoritative': ('authoritative' in config),
- 'subnet4': []
+ 'subnet4': [],
+ 'user-context': {}
}
if 'option' in config:
@@ -907,6 +886,9 @@ def kea_shared_network_json(shared_networks):
if 'bootfile_server' in config['option']:
network['next-server'] = config['option']['bootfile_server']
+ if 'ping_check' in config:
+ network['user-context']['enable-ping-check'] = True
+
if 'subnet' in config:
for subnet, subnet_config in config['subnet'].items():
if 'disable' in subnet_config:
diff --git a/python/vyos/vyconf_session.py b/python/vyos/vyconf_session.py
new file mode 100644
index 000000000..506095625
--- /dev/null
+++ b/python/vyos/vyconf_session.py
@@ -0,0 +1,123 @@
+# Copyright 2025 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 tempfile
+import shutil
+from functools import wraps
+from typing import Type
+
+from vyos.proto import vyconf_client
+from vyos.migrate import ConfigMigrate
+from vyos.migrate import ConfigMigrateError
+from vyos.component_version import append_system_version
+
+
+def output(o):
+ out = ''
+ for res in (o.output, o.error, o.warning):
+ if res is not None:
+ out = out + res
+ return out
+
+
+class VyconfSession:
+ def __init__(self, token: str = None, on_error: Type[Exception] = None):
+ if token is None:
+ out = vyconf_client.send_request('setup_session')
+ self.__token = out.output
+ else:
+ self.__token = token
+
+ self.on_error = on_error
+
+ @staticmethod
+ def raise_exception(f):
+ @wraps(f)
+ def wrapped(self, *args, **kwargs):
+ if self.on_error is None:
+ return f(self, *args, **kwargs)
+ o, e = f(self, *args, **kwargs)
+ if e:
+ raise self.on_error(o)
+ return o, e
+
+ return wrapped
+
+ @raise_exception
+ def set(self, path: list[str]) -> tuple[str, int]:
+ out = vyconf_client.send_request('set', token=self.__token, path=path)
+ return output(out), out.status
+
+ @raise_exception
+ def delete(self, path: list[str]) -> tuple[str, int]:
+ out = vyconf_client.send_request('delete', token=self.__token, path=path)
+ return output(out), out.status
+
+ @raise_exception
+ def commit(self) -> tuple[str, int]:
+ out = vyconf_client.send_request('commit', token=self.__token)
+ return output(out), out.status
+
+ @raise_exception
+ def discard(self) -> tuple[str, int]:
+ out = vyconf_client.send_request('discard', token=self.__token)
+ return output(out), out.status
+
+ def session_changed(self) -> bool:
+ out = vyconf_client.send_request('session_changed', token=self.__token)
+ return not bool(out.status)
+
+ @raise_exception
+ def load_config(self, file: str, migrate: bool = False) -> tuple[str, int]:
+ # pylint: disable=consider-using-with
+ if migrate:
+ tmp = tempfile.NamedTemporaryFile()
+ shutil.copy2(file, tmp.name)
+ config_migrate = ConfigMigrate(tmp.name)
+ try:
+ config_migrate.run()
+ except ConfigMigrateError as e:
+ tmp.close()
+ return repr(e), 1
+ file = tmp.name
+ else:
+ tmp = ''
+
+ out = vyconf_client.send_request('load', token=self.__token, location=file)
+ if tmp:
+ tmp.close()
+
+ return output(out), out.status
+
+ @raise_exception
+ def save_config(self, file: str, append_version: bool = False) -> tuple[str, int]:
+ out = vyconf_client.send_request('save', token=self.__token, location=file)
+ if append_version:
+ append_system_version(file)
+ return output(out), out.status
+
+ @raise_exception
+ def show_config(self, path: list[str] = None) -> tuple[str, int]:
+ if path is None:
+ path = []
+ out = vyconf_client.send_request('show_config', token=self.__token, path=path)
+ return output(out), out.status
+
+ def __del__(self):
+ out = vyconf_client.send_request('teardown', token=self.__token)
+ if out.status:
+ print(f'Could not tear down session {self.__token}: {output(out)}')
diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py
index 7bb850b22..935be6227 100755
--- a/smoketest/scripts/cli/test_service_dhcp-server.py
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -32,7 +32,6 @@ from vyos.template import inc_ip
from vyos.template import dec_ip
PROCESS_NAME = 'kea-dhcp4'
-CTRL_PROCESS_NAME = 'kea-ctrl-agent'
KEA4_CONF = '/run/kea/kea-dhcp4.conf'
KEA4_CTRL = '/run/kea/dhcp4-ctrl-socket'
HOSTSD_CLIENT = '/usr/bin/vyos-hostsd-client'
@@ -96,6 +95,10 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.assertTrue(key in base_obj)
self.assertEqual(base_obj[key], value)
+ def verify_service_running(self):
+ tmp = cmd('tail -n 100 /var/log/messages | grep kea')
+ self.assertTrue(process_named_running(PROCESS_NAME), msg=f'Service not running, log: {tmp}')
+
def test_dhcp_single_pool_range(self):
shared_net_name = 'SMOKE-1'
@@ -106,9 +109,12 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['listen-interface', interface])
+ self.cli_set(base_path + ['shared-network-name', shared_net_name, 'ping-check'])
+
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
self.cli_set(pool + ['subnet-id', '1'])
self.cli_set(pool + ['ignore-client-id'])
+ self.cli_set(pool + ['ping-check'])
# we use the first subnet IP address as default gateway
self.cli_set(pool + ['option', 'default-router', router])
self.cli_set(pool + ['option', 'name-server', dns_1])
@@ -151,6 +157,21 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400
)
+ # Verify ping-check
+ self.verify_config_value(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'user-context'],
+ 'enable-ping-check',
+ True
+ )
+
+ self.verify_config_value(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'user-context'],
+ 'enable-ping-check',
+ True
+ )
+
# Verify options
self.verify_config_object(
obj,
@@ -181,7 +202,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
)
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_single_pool_options(self):
shared_net_name = 'SMOKE-0815'
@@ -221,8 +242,11 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
pool + ['option', 'capwap-controller', capwap_access_controller]
)
+ static_route = '10.0.0.0/24'
+ static_route_nexthop = '192.0.2.1'
+
self.cli_set(
- pool + ['option', 'static-route', '10.0.0.0/24', 'next-hop', '192.0.2.1']
+ pool + ['option', 'static-route', static_route, 'next-hop', static_route_nexthop]
)
self.cli_set(pool + ['option', 'ipv6-only-preferred', ipv6_only_preferred])
self.cli_set(pool + ['option', 'time-zone', 'Europe/London'])
@@ -321,18 +345,13 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
obj,
['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
{
- 'name': 'rfc3442-static-route',
- 'data': '24,10,0,0,192,0,2,1, 0,192,0,2,1',
+ 'name': 'classless-static-route',
+ 'data': f'{static_route} - {static_route_nexthop}, 0.0.0.0/0 - {router}',
},
)
self.verify_config_object(
obj,
['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'windows-static-route', 'data': '24,10,0,0,192,0,2,1'},
- )
- self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
{'name': 'v6-only-preferred', 'data': ipv6_only_preferred},
)
self.verify_config_object(
@@ -361,7 +380,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
)
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_single_pool_options_scoped(self):
shared_net_name = 'SMOKE-2'
@@ -447,7 +466,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
)
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_single_pool_static_mapping(self):
shared_net_name = 'SMOKE-2'
@@ -593,7 +612,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
client_base += 1
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_multiple_pools(self):
lease_time = '14400'
@@ -735,7 +754,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
client_base += 1
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_exclude_not_in_range(self):
# T3180: verify else path when slicing DHCP ranges and exclude address
@@ -782,7 +801,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
)
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_exclude_in_range(self):
# T3180: verify else path when slicing DHCP ranges and exclude address
@@ -845,7 +864,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
)
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_relay_server(self):
# Listen on specific address and return DHCP leases from a non
@@ -893,7 +912,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
)
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_high_availability(self):
shared_net_name = 'FAILOVER'
@@ -996,8 +1015,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
)
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
- self.assertTrue(process_named_running(CTRL_PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_high_availability_standby(self):
shared_net_name = 'FAILOVER'
@@ -1096,8 +1114,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
)
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
- self.assertTrue(process_named_running(CTRL_PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_on_interface_with_vrf(self):
self.cli_set(['interfaces', 'ethernet', 'eth1', 'address', '10.1.1.1/30'])
@@ -1259,7 +1276,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
)
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
# All up and running, now test vyos-hostsd store
diff --git a/smoketest/scripts/cli/test_service_ids_ddos-protection.py b/smoketest/scripts/cli/test_service_ids_ddos-protection.py
deleted file mode 100755
index 91b056eea..000000000
--- a/smoketest/scripts/cli/test_service_ids_ddos-protection.py
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2022 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-import unittest
-
-from base_vyostest_shim import VyOSUnitTestSHIM
-
-from vyos.configsession import ConfigSessionError
-from vyos.utils.process import process_named_running
-from vyos.utils.file import read_file
-
-PROCESS_NAME = 'fastnetmon'
-FASTNETMON_CONF = '/run/fastnetmon/fastnetmon.conf'
-NETWORKS_CONF = '/run/fastnetmon/networks_list'
-EXCLUDED_NETWORKS_CONF = '/run/fastnetmon/excluded_networks_list'
-base_path = ['service', 'ids', 'ddos-protection']
-
-class TestServiceIDS(VyOSUnitTestSHIM.TestCase):
- @classmethod
- def setUpClass(cls):
- super(TestServiceIDS, cls).setUpClass()
-
- # ensure we can also run this test on a live system - so lets clean
- # out the current configuration :)
- cls.cli_delete(cls, base_path)
-
- def tearDown(self):
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
- # delete test config
- self.cli_delete(base_path)
- self.cli_commit()
-
- self.assertFalse(os.path.exists(FASTNETMON_CONF))
- self.assertFalse(process_named_running(PROCESS_NAME))
-
- def test_fastnetmon(self):
- networks = ['10.0.0.0/24', '10.5.5.0/24', '2001:db8:10::/64', '2001:db8:20::/64']
- excluded_networks = ['10.0.0.1/32', '2001:db8:10::1/128']
- interfaces = ['eth0', 'eth1']
- fps = '3500'
- mbps = '300'
- pps = '60000'
-
- self.cli_set(base_path + ['mode', 'mirror'])
- # Required network!
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- for tmp in networks:
- self.cli_set(base_path + ['network', tmp])
-
- # optional excluded-network!
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- for tmp in excluded_networks:
- self.cli_set(base_path + ['excluded-network', tmp])
-
- # Required interface(s)!
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- for tmp in interfaces:
- self.cli_set(base_path + ['listen-interface', tmp])
-
- self.cli_set(base_path + ['direction', 'in'])
- self.cli_set(base_path + ['threshold', 'general', 'fps', fps])
- self.cli_set(base_path + ['threshold', 'general', 'pps', pps])
- self.cli_set(base_path + ['threshold', 'general', 'mbps', mbps])
-
- # commit changes
- self.cli_commit()
-
- # Check configured port
- config = read_file(FASTNETMON_CONF)
- self.assertIn(f'mirror_afpacket = on', config)
- self.assertIn(f'process_incoming_traffic = on', config)
- self.assertIn(f'process_outgoing_traffic = off', config)
- self.assertIn(f'ban_for_flows = on', config)
- self.assertIn(f'threshold_flows = {fps}', config)
- self.assertIn(f'ban_for_bandwidth = on', config)
- 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)
-
- excluded_network_config = read_file(EXCLUDED_NETWORKS_CONF)
- for tmp in excluded_networks:
- self.assertIn(f'{tmp}', excluded_network_config)
-
-if __name__ == '__main__':
- unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index 91a76e6f6..c1d943bde 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -352,6 +352,94 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.tearDownPKI()
+ def test_site_to_site_vti_ts_afi(self):
+ local_address = '192.0.2.10'
+ vti = 'vti10'
+ # IKE
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'disable-mobike'])
+ # ESP
+ self.cli_set(base_path + ['esp-group', esp_group, 'compression'])
+ # VTI interface
+ self.cli_set(vti_path + [vti, 'address', '10.1.1.1/24'])
+
+ # vpn ipsec auth psk <tag> id <x.x.x.x>
+ self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', local_id])
+ self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', remote_id])
+ self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', peer_ip])
+ self.cli_set(base_path + ['authentication', 'psk', connection_name, 'secret', secret])
+
+ # Site to site
+ peer_base_path = base_path + ['site-to-site', 'peer', connection_name]
+ self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
+ self.cli_set(peer_base_path + ['connection-type', 'none'])
+ self.cli_set(peer_base_path + ['force-udp-encapsulation'])
+ self.cli_set(peer_base_path + ['ike-group', ike_group])
+ self.cli_set(peer_base_path + ['default-esp-group', esp_group])
+ self.cli_set(peer_base_path + ['local-address', local_address])
+ self.cli_set(peer_base_path + ['remote-address', peer_ip])
+ self.cli_set(peer_base_path + ['vti', 'bind', vti])
+ self.cli_set(peer_base_path + ['vti', 'esp-group', esp_group])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'local', 'prefix', '0.0.0.0/0'])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'remote', 'prefix', '192.0.2.1/32'])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'remote', 'prefix', '192.0.2.3/32'])
+
+ self.cli_commit()
+
+ swanctl_conf = read_file(swanctl_file)
+ if_id = vti.lstrip('vti')
+ # The key defaults to 0 and will match any policies which similarly do
+ # not have a lookup key configuration - thus we shift the key by one
+ # to also support a vti0 interface
+ if_id = str(int(if_id) +1)
+ swanctl_conf_lines = [
+ f'version = 2',
+ f'auth = psk',
+ f'proposals = aes128-sha1-modp1024',
+ f'esp_proposals = aes128-sha1-modp1024',
+ f'local_addrs = {local_address} # dhcp:no',
+ f'mobike = no',
+ f'remote_addrs = {peer_ip}',
+ f'mode = tunnel',
+ f'local_ts = 0.0.0.0/0',
+ f'remote_ts = 192.0.2.1/32,192.0.2.3/32',
+ f'ipcomp = yes',
+ f'start_action = none',
+ f'replay_window = 32',
+ f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one
+ f'if_id_out = {if_id}',
+ f'updown = "/etc/ipsec.d/vti-up-down {vti}"'
+ ]
+ for line in swanctl_conf_lines:
+ self.assertIn(line, swanctl_conf)
+
+ # Check IPv6 TS
+ self.cli_delete(peer_base_path + ['vti', 'traffic-selector'])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'local', 'prefix', '::/0'])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'remote', 'prefix', '::/0'])
+ self.cli_commit()
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_conf_lines = [
+ f'local_ts = ::/0',
+ f'remote_ts = ::/0',
+ f'updown = "/etc/ipsec.d/vti-up-down {vti}"'
+ ]
+ for line in swanctl_conf_lines:
+ self.assertIn(line, swanctl_conf)
+
+ # Check both TS (IPv4 + IPv6)
+ self.cli_delete(peer_base_path + ['vti', 'traffic-selector'])
+ self.cli_commit()
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_conf_lines = [
+ f'local_ts = 0.0.0.0/0,::/0',
+ f'remote_ts = 0.0.0.0/0,::/0',
+ f'updown = "/etc/ipsec.d/vti-up-down {vti}"'
+ ]
+ for line in swanctl_conf_lines:
+ self.assertIn(line, swanctl_conf)
+
+
def test_dmvpn(self):
ike_lifetime = '3600'
esp_lifetime = '1800'
diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py
index 5a729af74..e46d916fd 100755
--- a/src/conf_mode/service_dhcp-server.py
+++ b/src/conf_mode/service_dhcp-server.py
@@ -41,7 +41,6 @@ from vyos import airbag
airbag.enable()
-ctrl_config_file = '/run/kea/kea-ctrl-agent.conf'
ctrl_socket = '/run/kea/dhcp4-ctrl-socket'
config_file = '/run/kea/kea-dhcp4.conf'
lease_file = '/config/dhcp/dhcp4-leases.csv'
@@ -480,13 +479,6 @@ def generate(dhcp):
dhcp['high_availability']['ca_cert_file'] = ca_cert_file
render(
- ctrl_config_file,
- 'dhcp-server/kea-ctrl-agent.conf.j2',
- dhcp,
- user=user_group,
- group=user_group,
- )
- render(
config_file,
'dhcp-server/kea-dhcp4.conf.j2',
dhcp,
@@ -498,7 +490,7 @@ def generate(dhcp):
def apply(dhcp):
- services = ['kea-ctrl-agent', 'kea-dhcp4-server', 'kea-dhcp-ddns-server']
+ services = ['kea-dhcp4-server', 'kea-dhcp-ddns-server']
if not dhcp or 'disable' in dhcp:
for service in services:
@@ -515,9 +507,6 @@ def apply(dhcp):
if service == 'kea-dhcp-ddns-server' and 'dynamic_dns_update' not in dhcp:
action = 'stop'
- if service == 'kea-ctrl-agent' and 'high_availability' not in dhcp:
- action = 'stop'
-
call(f'systemctl {action} {service}.service')
return None
diff --git a/src/conf_mode/service_ids_ddos-protection.py b/src/conf_mode/service_ids_ddos-protection.py
deleted file mode 100755
index 276a71fcb..000000000
--- a/src/conf_mode/service_ids_ddos-protection.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018-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 os
-
-from sys import exit
-
-from vyos.config import Config
-from vyos.template import render
-from vyos.utils.process import call
-from vyos import ConfigError
-from vyos import airbag
-airbag.enable()
-
-config_file = r'/run/fastnetmon/fastnetmon.conf'
-networks_list = r'/run/fastnetmon/networks_list'
-excluded_networks_list = r'/run/fastnetmon/excluded_networks_list'
-attack_dir = '/var/log/fastnetmon_attacks'
-
-def get_config(config=None):
- if config:
- conf = config
- 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,
- with_recursive_defaults=True)
-
- return fastnetmon
-
-def verify(fastnetmon):
- if not fastnetmon:
- return None
-
- if 'mode' not in fastnetmon:
- raise ConfigError('Specify operating mode!')
-
- if fastnetmon.get('mode') == 'mirror' and 'listen_interface' not in fastnetmon:
- raise ConfigError("Incorrect settings for 'mode mirror': must specify interface(s) for traffic mirroring")
-
- if fastnetmon.get('mode') == 'sflow' and 'listen_address' not in fastnetmon.get('sflow', {}):
- raise ConfigError("Incorrect settings for 'mode sflow': must specify sFlow 'listen-address'")
-
- 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 "{alert_script}" is not executable!'.format(fastnetmon['alert_script']))
- else:
- raise ConfigError('File "{alert_script}" does not exists!'.format(fastnetmon))
-
-def generate(fastnetmon):
- if not fastnetmon:
- for file in [config_file, networks_list]:
- if os.path.isfile(file):
- os.unlink(file)
-
- return None
-
- # Create dir for log attack details
- if not os.path.exists(attack_dir):
- os.mkdir(attack_dir)
-
- render(config_file, 'ids/fastnetmon.j2', fastnetmon)
- render(networks_list, 'ids/fastnetmon_networks_list.j2', fastnetmon)
- render(excluded_networks_list, 'ids/fastnetmon_excluded_networks_list.j2', fastnetmon)
- return None
-
-def apply(fastnetmon):
- systemd_service = 'fastnetmon.service'
- if not fastnetmon:
- # Stop fastnetmon service if removed
- call(f'systemctl stop {systemd_service}')
- else:
- call(f'systemctl reload-or-restart {systemd_service}')
-
- return None
-
-if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- exit(1)
diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py
index 064a1aa91..b45a9d8a6 100755
--- a/src/conf_mode/system_option.py
+++ b/src/conf_mode/system_option.py
@@ -122,6 +122,10 @@ def generate(options):
render(ssh_config, 'system/ssh_config.j2', options)
render(usb_autosuspend, 'system/40_usb_autosuspend.j2', options)
+ # XXX: This code path and if statements must be kept in sync with the Kernel
+ # option handling in image_installer.py:get_cli_kernel_options(). This
+ # occurance is used for having the appropriate options passed to GRUB
+ # when re-configuring options on the CLI.
cmdline_options = []
if 'kernel' in options:
if 'disable_mitigations' in options['kernel']:
@@ -131,8 +135,7 @@ def generate(options):
if 'amd_pstate_driver' in options['kernel']:
mode = options['kernel']['amd_pstate_driver']
cmdline_options.append(
- f'initcall_blacklist=acpi_cpufreq_init amd_pstate={mode}'
- )
+ f'initcall_blacklist=acpi_cpufreq_init amd_pstate={mode}')
grub_util.update_kernel_cmdline_options(' '.join(cmdline_options))
return None
diff --git a/src/etc/netplug/vyos-netplug-dhcp-client b/src/etc/netplug/vyos-netplug-dhcp-client
index 7fe6cda75..a230fe900 100755
--- a/src/etc/netplug/vyos-netplug-dhcp-client
+++ b/src/etc/netplug/vyos-netplug-dhcp-client
@@ -20,10 +20,10 @@ import sys
from time import sleep
from vyos.config import Config
-from vyos.configdict import get_interface_dict
-from vyos.ifconfig import Interface
from vyos.ifconfig import Section
from vyos.utils.boot import boot_configuration_complete
+from vyos.utils.process import cmd
+from vyos.utils.process import is_systemd_service_active
from vyos.utils.commit import commit_in_progress
from vyos import airbag
@@ -38,21 +38,34 @@ if not boot_configuration_complete():
sys.exit(1)
interface = sys.argv[1]
-# helper scripts should only work on physical interfaces not on individual
-# sub-interfaces. Moving e.g. a VLAN interface in/out a VRF will also trigger
-# this script which should be prohibited - bail out early
-if '.' in interface:
- sys.exit(0)
while commit_in_progress():
- sleep(1)
+ sleep(0.250)
in_out = sys.argv[2]
config = Config()
interface_path = ['interfaces'] + Section.get_config_path(interface).split()
-_, interface_config = get_interface_dict(
- config, interface_path[:-1], ifname=interface, with_pki=True
-)
-if 'deleted' not in interface_config:
- Interface(interface).update(interface_config)
+
+systemdV4_service = f'dhclient@{interface}.service'
+systemdV6_service = f'dhcp6c@{interface}.service'
+if in_out == 'out':
+ # Interface moved state to down
+ if is_systemd_service_active(systemdV4_service):
+ cmd(f'systemctl stop {systemdV4_service}')
+ if is_systemd_service_active(systemdV6_service):
+ cmd(f'systemctl stop {systemdV6_service}')
+elif in_out == 'in':
+ if config.exists_effective(interface_path + ['address']):
+ tmp = config.return_effective_values(interface_path + ['address'])
+ # Always (re-)start the DHCP(v6) client service. If the DHCP(v6) client
+ # is already running - which could happen if the interface is re-
+ # configured in operational down state, it will have a backoff
+ # time increasing while not receiving a DHCP(v6) reply.
+ #
+ # To make the interface instantly available, and as for a DHCP(v6) lease
+ # we will re-start the service and thus cancel the backoff time.
+ if 'dhcp' in tmp:
+ cmd(f'systemctl restart {systemdV4_service}')
+ if 'dhcpv6' in tmp:
+ cmd(f'systemctl restart {systemdV6_service}')
diff --git a/src/etc/systemd/system/fastnetmon.service.d/override.conf b/src/etc/systemd/system/fastnetmon.service.d/override.conf
deleted file mode 100644
index 841666070..000000000
--- a/src/etc/systemd/system/fastnetmon.service.d/override.conf
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-RequiresMountsFor=/run
-ConditionPathExists=/run/fastnetmon/fastnetmon.conf
-After=
-After=vyos-router.service
-
-[Service]
-Type=simple
-WorkingDirectory=/run/fastnetmon
-PIDFile=/run/fastnetmon.pid
-ExecStart=
-ExecStart=/usr/sbin/fastnetmon --configuration_file /run/fastnetmon/fastnetmon.conf
diff --git a/src/etc/systemd/system/kea-ctrl-agent.service.d/override.conf b/src/etc/systemd/system/kea-ctrl-agent.service.d/override.conf
deleted file mode 100644
index c74fafb42..000000000
--- a/src/etc/systemd/system/kea-ctrl-agent.service.d/override.conf
+++ /dev/null
@@ -1,10 +0,0 @@
-[Unit]
-After=
-After=vyos-router.service
-ConditionFileNotEmpty=
-
-[Service]
-ExecStart=
-ExecStart=/usr/sbin/kea-ctrl-agent -c /run/kea/kea-ctrl-agent.conf
-AmbientCapabilities=CAP_NET_BIND_SERVICE
-CapabilityBoundingSet=CAP_NET_BIND_SERVICE
diff --git a/src/init/vyos-router b/src/init/vyos-router
index 565b778e6..081adf214 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -557,6 +557,9 @@ start ()
if [[ ! -z "$tmp" ]]; then
vtysh -c "rpki start"
fi
+
+ # Start netplug daemon
+ systemctl start netplug.service
}
stop()
@@ -574,8 +577,8 @@ stop()
umount ${vyatta_configdir}
log_action_end_msg $?
+ systemctl stop netplug.service
systemctl stop vyconfd.service
-
systemctl stop frr.service
unmount_encrypted_config
diff --git a/src/migration-scripts/dhcp-server/7-to-8 b/src/migration-scripts/dhcp-server/7-to-8
index 7fcb62e86..d0f9455bb 100644
--- a/src/migration-scripts/dhcp-server/7-to-8
+++ b/src/migration-scripts/dhcp-server/7-to-8
@@ -41,9 +41,6 @@ def migrate(config: ConfigTree) -> None:
for network in config.list_nodes(base + ['shared-network-name']):
base_network = base + ['shared-network-name', network]
- if config.exists(base_network + ['ping-check']):
- config.delete(base_network + ['ping-check'])
-
if config.exists(base_network + ['shared-network-parameters']):
config.delete(base_network +['shared-network-parameters'])
@@ -57,9 +54,6 @@ def migrate(config: ConfigTree) -> None:
if config.exists(base_subnet + ['enable-failover']):
config.delete(base_subnet + ['enable-failover'])
- if config.exists(base_subnet + ['ping-check']):
- config.delete(base_subnet + ['ping-check'])
-
if config.exists(base_subnet + ['subnet-parameters']):
config.delete(base_subnet + ['subnet-parameters'])
diff --git a/src/migration-scripts/ids/1-to-2 b/src/migration-scripts/ids/1-to-2
new file mode 100644
index 000000000..4c0333c88
--- /dev/null
+++ b/src/migration-scripts/ids/1-to-2
@@ -0,0 +1,30 @@
+# Copyright 2025 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/>.
+
+# T: Migrate threshold and add new threshold types
+
+from vyos.configtree import ConfigTree
+
+# The old 'service ids' path was only used for FastNetMon
+# Suricata is in 'service suricata',
+# so this isn't an overreach
+base = ['service', 'ids']
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+ else:
+ config.delete(base)
diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py
index 179913f15..2660309a5 100755
--- a/src/op_mode/image_installer.py
+++ b/src/op_mode/image_installer.py
@@ -24,7 +24,9 @@ from glob import glob
from sys import exit
from os import environ
from os import readlink
-from os import getpid, getppid
+from os import getpid
+from os import getppid
+from json import loads
from typing import Union
from urllib.parse import urlparse
from passlib.hosts import linux_context
@@ -35,15 +37,23 @@ from psutil import disk_partitions
from vyos.base import Warning
from vyos.configtree import ConfigTree
from vyos.remote import download
-from vyos.system import disk, grub, image, compat, raid, SYSTEM_CFG_VER
+from vyos.system import disk
+from vyos.system import grub
+from vyos.system import image
+from vyos.system import compat
+from vyos.system import raid
+from vyos.system import SYSTEM_CFG_VER
+from vyos.system import grub_util
from vyos.template import render
from vyos.utils.auth import (
DEFAULT_PASSWORD,
EPasswdStrength,
evaluate_strength
)
+from vyos.utils.dict import dict_search
from vyos.utils.io import ask_input, ask_yes_no, select_entry
from vyos.utils.file import chmod_2775
+from vyos.utils.file import read_file
from vyos.utils.process import cmd, run, rc_cmd
from vyos.version import get_version_data
@@ -477,6 +487,25 @@ def setup_grub(root_dir: str) -> None:
render(grub_cfg_menu, grub.TMPL_GRUB_MENU, {})
render(grub_cfg_options, grub.TMPL_GRUB_OPTS, {})
+def get_cli_kernel_options(config_file: str) -> list:
+ config = ConfigTree(read_file(config_file))
+ config_dict = loads(config.to_json())
+ kernel_options = dict_search('system.option.kernel', config_dict)
+ cmdline_options = []
+
+ # XXX: This code path and if statements must be kept in sync with the Kernel
+ # option handling in system_options.py:generate(). This occurance is used
+ # for having the appropriate options passed to GRUB after an image upgrade!
+ if 'disable-mitigations' in kernel_options:
+ cmdline_options.append('mitigations=off')
+ if 'disable-power-saving' in kernel_options:
+ cmdline_options.append('intel_idle.max_cstate=0 processor.max_cstate=1')
+ if 'amd-pstate-driver' in kernel_options:
+ mode = kernel_options['amd-pstate-driver']
+ cmdline_options.append(
+ f'initcall_blacklist=acpi_cpufreq_init amd_pstate={mode}')
+
+ return cmdline_options
def configure_authentication(config_file: str, password: str) -> None:
"""Write encrypted password to config file
@@ -491,10 +520,7 @@ def configure_authentication(config_file: str, password: str) -> None:
plaintext exposed
"""
encrypted_password = linux_context.hash(password)
-
- with open(config_file) as f:
- config_string = f.read()
-
+ config_string = read_file(config_file)
config = ConfigTree(config_string)
config.set([
'system', 'login', 'user', 'vyos', 'authentication',
@@ -1045,6 +1071,12 @@ def add_image(image_path: str, vrf: str = None, username: str = '',
if set_as_default:
grub.set_default(image_name, root_dir)
+ cmdline_options = get_cli_kernel_options(
+ f'{target_config_dir}/config.boot')
+ grub_util.update_kernel_cmdline_options(' '.join(cmdline_options),
+ root_dir=root_dir,
+ version=image_name)
+
except OSError as e:
# if no space error, remove image dir and cleanup
if e.errno == ENOSPC:
diff --git a/src/systemd/netplug.service b/src/systemd/netplug.service
new file mode 100644
index 000000000..928c553e8
--- /dev/null
+++ b/src/systemd/netplug.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=Network cable hotplug management daemon
+Documentation=man:netplugd(8)
+After=vyos-router.service
+
+[Service]
+Type=forking
+PIDFile=/run/netplugd.pid
+ExecStart=/sbin/netplugd -c /etc/netplug/netplugd.conf -p /run/netplugd.pid
diff --git a/src/systemd/vyos.target b/src/systemd/vyos.target
index 47c91c1cc..c5d04891d 100644
--- a/src/systemd/vyos.target
+++ b/src/systemd/vyos.target
@@ -1,3 +1,3 @@
[Unit]
Description=VyOS target
-After=multi-user.target
+After=multi-user.target vyos-grub-update.service