summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--data/templates/firewall/nftables-nat.tmpl31
-rw-r--r--data/templates/frr/bgp.frr.tmpl4
-rw-r--r--data/templates/frr/ospf.frr.tmpl2
-rw-r--r--data/templates/ntp/ntpd.conf.tmpl (renamed from data/templates/ntp/ntp.conf.tmpl)0
-rw-r--r--data/templates/ntp/override.conf.tmpl5
-rw-r--r--data/templates/openvpn/server.conf.tmpl6
-rw-r--r--data/templates/ssh/override.conf.tmpl1
-rw-r--r--data/templates/vrf/vrf.conf.tmpl9
-rw-r--r--debian/vyos-1x.postinst28
-rw-r--r--interface-definitions/dhcp-server.xml.in7
-rw-r--r--interface-definitions/include/ospf-metric.xml.i33
-rw-r--r--interface-definitions/interfaces-bridge.xml.in9
-rw-r--r--interface-definitions/protocols-bgp.xml.in10
-rw-r--r--interface-definitions/protocols-ospf.xml.in840
-rw-r--r--interface-definitions/vrf.xml.in1
-rw-r--r--python/vyos/configdict.py10
-rw-r--r--python/vyos/ifconfig/bridge.py122
-rw-r--r--python/vyos/ifconfig/interface.py69
-rw-r--r--python/vyos/ifconfig/tunnel.py1
-rw-r--r--python/vyos/template.py43
-rwxr-xr-xscripts/override-default101
-rw-r--r--smoketest/configs/bgp-ixp (renamed from smoketest/configs/bgp-ix-router)0
-rw-r--r--smoketest/configs/bgp-rpki116
-rw-r--r--smoketest/configs/dmz-guest-lan-nat-pppoe-router1666
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py59
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bonding.py4
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bridge.py33
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_ethernet.py11
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_loopback.py14
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_tunnel.py31
-rwxr-xr-xsmoketest/scripts/cli/test_service_ssh.py10
-rwxr-xr-xsmoketest/scripts/cli/test_system_ip.py2
-rwxr-xr-xsmoketest/scripts/cli/test_system_ipv6.py102
-rwxr-xr-xsmoketest/scripts/cli/test_system_ntp.py6
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py90
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py92
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py12
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py2
-rwxr-xr-xsrc/conf_mode/nat.py9
-rwxr-xr-xsrc/conf_mode/ntp.py11
-rwxr-xr-xsrc/conf_mode/policy-local-route.py2
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py42
-rw-r--r--src/conf_mode/protocols_ospf.py103
-rwxr-xr-xsrc/conf_mode/ssh.py20
-rwxr-xr-xsrc/conf_mode/system-option.py14
-rwxr-xr-xsrc/conf_mode/vrf.py241
-rw-r--r--src/etc/sysctl.d/30-vyos-router.conf98
-rw-r--r--src/etc/udev/rules.d/42-qemu-usb.rules14
-rw-r--r--src/etc/udev/rules.d/63-hyperv-vf-net.rules5
-rw-r--r--src/etc/udev/rules.d/64-vyos-vmware-net.rules14
-rw-r--r--src/etc/udev/rules.d/65-vyatta-net.rules26
-rw-r--r--src/tests/test_template.py19
-rwxr-xr-xsrc/validators/allowed-vlan19
54 files changed, 3788 insertions, 434 deletions
diff --git a/Makefile b/Makefile
index 00d7b5e21..a063d1df6 100644
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,8 @@ op_xml_obj = $(op_xml_src:.xml.in=.xml)
interface_definitions: $(config_xml_obj)
mkdir -p $(TMPL_DIR)
+ $(CURDIR)/scripts/override-default $(BUILD_DIR)/interface-definitions
+
find $(BUILD_DIR)/interface-definitions -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-templates {} $(CURDIR)/schema/interface_definition.rng $(TMPL_DIR) || exit 1
# XXX: delete top level node.def's that now live in other packages
@@ -45,6 +47,7 @@ interface_definitions: $(config_xml_obj)
rm -f $(TMPL_DIR)/vpn/node.def
rm -f $(TMPL_DIR)/vpn/ipsec/node.def
rm -rf $(TMPL_DIR)/vpn/nipsec
+ rm -rf $(TMPL_DIR)/protocols/nospf
# XXX: required until OSPF and RIP is migrated from vyatta-cfg-quagga to vyos-1x
mkdir $(TMPL_DIR)/interfaces/loopback/node.tag/ipv6
diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl
index 770a24a95..5480447f2 100644
--- a/data/templates/firewall/nftables-nat.tmpl
+++ b/data/templates/firewall/nftables-nat.tmpl
@@ -21,18 +21,34 @@
{% set comment = 'DST-NAT-' + rule %}
{% set base_log = '[NAT-DST-' + rule %}
{% set interface = ' iifname "' + config.inbound_interface + '"' if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %}
-{% set trns_addr = 'dnat to ' + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
+{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
+{# support 1:1 network translation #}
+{% if config.translation.address | is_ip_network %}
+{% set trns_addr = 'dnat ip prefix to ip daddr map { ' + config.source.address + ' : ' + config.translation.address + ' }' %}
+{# we can now clear out the src_addr part as it's already covered in aboves map #}
+{% set src_addr = '' %}
+{% else %}
+{% set trns_addr = 'dnat to ' + config.translation.address %}
+{% endif %}
+{% endif %}
{% elif chain == 'POSTROUTING' %}
{% set comment = 'SRC-NAT-' + rule %}
{% set base_log = '[NAT-SRC-' + rule %}
{% set interface = ' oifname "' + config.outbound_interface + '"' if config.outbound_interface is defined and config.outbound_interface != 'any' else '' %}
-{% if config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %}
-{% set trns_addr = config.translation.address %}
-{% if config.translation.port is defined and config.translation.port is not none %}
-{% set trns_addr = trns_addr + ' to ' %}
+{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
+{% if config.translation.address == 'masquerade' %}
+{% set trns_addr = config.translation.address %}
+{% if config.translation.port is defined and config.translation.port is not none %}
+{% set trns_addr = trns_addr + ' to ' %}
+{% endif %}
+{# support 1:1 network translation #}
+{% elif config.translation.address | is_ip_network %}
+{% set trns_addr = 'snat ip prefix to ip saddr map { ' + config.source.address + ' : ' + config.translation.address + ' }' %}
+{# we can now clear out the src_addr part as it's already covered in aboves map #}
+{% set src_addr = '' %}
+{% else %}
+{% set trns_addr = 'snat to ' + config.translation.address %}
{% endif %}
-{% else %}
-{% set trns_addr = 'snat to ' + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
{% endif %}
{% endif %}
{% set trns_port = ':' + config.translation.port if config.translation is defined and config.translation.port is defined and config.translation.port is not none %}
@@ -132,7 +148,6 @@ add rule ip raw NAT_CONNTRACK counter accept
{{ nat_rule(rule, config, 'PREROUTING') }}
{% endfor %}
{% endif %}
-
#
# Source NAT rules build up here
#
diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl
index b3aa8fe02..68731abab 100644
--- a/data/templates/frr/bgp.frr.tmpl
+++ b/data/templates/frr/bgp.frr.tmpl
@@ -198,8 +198,8 @@ router bgp {{ asn }}
{% endif %}
!
{% if neighbor is defined and neighbor is not none %}
-{% for n, config in neighbor.items() %}
-{{ bgp_neighbor(n, config) }}
+{% for peer, config in neighbor.items() %}
+{{ bgp_neighbor(peer, config) }}
{% endfor %}
{% endif %}
!
diff --git a/data/templates/frr/ospf.frr.tmpl b/data/templates/frr/ospf.frr.tmpl
new file mode 100644
index 000000000..465034f15
--- /dev/null
+++ b/data/templates/frr/ospf.frr.tmpl
@@ -0,0 +1,2 @@
+!
+!
diff --git a/data/templates/ntp/ntp.conf.tmpl b/data/templates/ntp/ntpd.conf.tmpl
index 2b56b53c3..2b56b53c3 100644
--- a/data/templates/ntp/ntp.conf.tmpl
+++ b/data/templates/ntp/ntpd.conf.tmpl
diff --git a/data/templates/ntp/override.conf.tmpl b/data/templates/ntp/override.conf.tmpl
index e0b947686..28eb61b21 100644
--- a/data/templates/ntp/override.conf.tmpl
+++ b/data/templates/ntp/override.conf.tmpl
@@ -1,11 +1,14 @@
{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %}
[Unit]
StartLimitIntervalSec=0
+ConditionPathExists={{config_file}}
After=vyos-router.service
[Service]
ExecStart=
-ExecStart={{vrf_command}}/usr/lib/ntp/ntp-systemd-wrapper
+ExecStart={{vrf_command}}/usr/sbin/ntpd -g -p {{config_file | replace('.conf', '.pid') }} -c {{config_file}} -u ntp:ntp
+PIDFile=
+PIDFile={{config_file | replace('.conf', '.pid') }}
Restart=always
RestartSec=10
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index b3b0c936a..79288e40f 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -281,6 +281,10 @@ compat-names
# Custom options added by user (not validated)
#
{% for option in openvpn_option %}
-{{ option }}
+{% for argument in option.split('--') %}
+{% if argument is defined and argument != '' %}
+--{{ argument }}
+{% endif %}
+{% endfor %}
{% endfor %}
{% endif %}
diff --git a/data/templates/ssh/override.conf.tmpl b/data/templates/ssh/override.conf.tmpl
index 0abde6248..5f8f35e89 100644
--- a/data/templates/ssh/override.conf.tmpl
+++ b/data/templates/ssh/override.conf.tmpl
@@ -8,5 +8,6 @@ ConditionPathExists={{config_file}}
ExecStart=
ExecStart={{vrf_command}}/usr/sbin/sshd -f {{config_file}} -D $SSHD_OPTS
Restart=always
+RestartPreventExitStatus=
RestartSec=10
RuntimeDirectoryPreserve=yes
diff --git a/data/templates/vrf/vrf.conf.tmpl b/data/templates/vrf/vrf.conf.tmpl
index 6d01d2b89..29c0ba08d 100644
--- a/data/templates/vrf/vrf.conf.tmpl
+++ b/data/templates/vrf/vrf.conf.tmpl
@@ -1,8 +1,9 @@
### Autogenerated by vrf.py ###
#
# Routing table ID to name mapping reference
-
# id vrf name comment
-{% for vrf in vrf_add %}
-{{ "%-10s" | format(vrf.table) }} {{ "%-16s" | format(vrf.name) }} # {{ vrf.description }}
-{% endfor %}
+{% if name is defined and name is not none %}
+{% for vrf, vrf_config in name.items() %}
+{{ "%-10s" | format(vrf_config.table) }} {{ "%-16s" | format(vrf) }} {{ '# ' + vrf_config.description if vrf_config.description is defined and vrf_config.description is not none }}
+{% endfor %}
+{% endif %}
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index 92948de12..5fadddc86 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -20,6 +20,34 @@ if ! grep -q '^minion' /etc/passwd; then
adduser --quiet minion users
fi
+# OpenVPN should get its own user
+if ! grep -q '^openvpn' /etc/passwd; then
+ adduser --quiet --firstuid 100 --system --group --shell /usr/sbin/nologin openvpn
+fi
+
+# Add RADIUS operator user for RADIUS authenticated users to map to
+if ! grep -q '^radius_user' /etc/passwd; then
+ adduser --quiet --firstuid 1001 --disabled-login --ingroup users --gecos "radius user" --shell /bin/vbash radius_user
+ adduser --quiet radius_user frrvty
+ adduser --quiet radius_user vyattaop
+ adduser --quiet radius_user operator
+ adduser --quiet radius_user adm
+ adduser --quiet radius_user dip
+ adduser --quiet radius_user users
+fi
+
+# Add RADIUS admin user for RADIUS authenticated users to map to
+if ! grep -q '^radius_priv_user' /etc/passwd; then
+ adduser --quiet --firstuid 1001 --disabled-login --ingroup vyattacfg --gecos "radius privileged user" --shell /bin/vbash radius_priv_user
+ adduser --quiet radius_priv_user frrvty
+ adduser --quiet radius_priv_user vyattacfg
+ adduser --quiet radius_priv_user sudo
+ adduser --quiet radius_priv_user adm
+ adduser --quiet radius_priv_user dip
+ adduser --quiet radius_priv_user disk
+ adduser --quiet radius_priv_user users
+fi
+
# add hostsd group for vyos-hostsd
if ! grep -q '^hostsd' /etc/group; then
addgroup --quiet --system hostsd
diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in
index 912e4eaf7..aba2bea8a 100644
--- a/interface-definitions/dhcp-server.xml.in
+++ b/interface-definitions/dhcp-server.xml.in
@@ -335,11 +335,14 @@
</leafNode>
<leafNode name="mac-address">
<properties>
- <help>MAC address of static mapping [REQUIRED]</help>
+ <help>Media Access Control (MAC) address</help>
<valueHelp>
<format>h:h:h:h:h:h</format>
- <description>MAC address used in static mapping [REQUIRED]</description>
+ <description>Hardware (MAC) address</description>
</valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
</properties>
</leafNode>
<leafNode name="static-mapping-parameters">
diff --git a/interface-definitions/include/ospf-metric.xml.i b/interface-definitions/include/ospf-metric.xml.i
new file mode 100644
index 000000000..771fda02d
--- /dev/null
+++ b/interface-definitions/include/ospf-metric.xml.i
@@ -0,0 +1,33 @@
+<!-- included start from ospf-metric.xml.i -->
+<leafNode name="metric">
+ <properties>
+ <help>OSPF default metric</help>
+ <valueHelp>
+ <format>u32:0-16777214</format>
+ <description>Default metric</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-16777214"/>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="metric-type">
+ <properties>
+ <help>OSPF metric type for default routes</help>
+ <valueHelp>
+ <format>u32:1-2</format>
+ <description>Metric type for default routes (default 2)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-2"/>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="route-map">
+ <properties>
+ <help>Route map reference</help>
+ <completionHelp>
+ <path>policy route-map</path>
+ </completionHelp>
+ </properties>
+</leafNode>
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index c32c0ca32..63c543f33 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -86,6 +86,12 @@
#include <include/interface-ipv6-options.xml.i>
#include <include/interface-mac.xml.i>
#include <include/interface-mirror.xml.i>
+ <leafNode name="enable-vlan">
+ <properties>
+ <help>Enable VLAN aware bridge</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="max-age">
<properties>
<help>Interval at which neighbor bridges are removed</help>
@@ -138,7 +144,7 @@
<description>VLAN id range allowed on this interface (use '-' as delimiter)</description>
</valueHelp>
<constraint>
- <regex>^([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})$</regex>
+ <validator name="allowed-vlan"/>
</constraint>
<constraintErrorMessage>not a valid VLAN ID value or range</constraintErrorMessage>
<multi/>
@@ -196,7 +202,6 @@
<valueless/>
</properties>
</leafNode>
- #include <include/vif-s.xml.i>
#include <include/vif.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/protocols-bgp.xml.in b/interface-definitions/protocols-bgp.xml.in
index 96fd74db7..12437128c 100644
--- a/interface-definitions/protocols-bgp.xml.in
+++ b/interface-definitions/protocols-bgp.xml.in
@@ -545,7 +545,7 @@
<help>Half-life time for dampening [REQUIRED]</help>
<valueHelp>
<format>u32:1-45</format>
- <description>Half-life penalty in seconds</description>
+ <description>Half-life penalty in minutes</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-45"/>
@@ -557,7 +557,7 @@
<help>Maximum duration to suppress a stable route [REQUIRED]</help>
<valueHelp>
<format>u32:1-255</format>
- <description>Maximum suppress duration in seconds</description>
+ <description>Maximum suppress duration in minutes</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-255"/>
@@ -566,10 +566,10 @@
</leafNode>
<leafNode name="re-use">
<properties>
- <help>Time to start reusing a route [REQUIRED]</help>
+ <help>Threshold to start reusing a route [REQUIRED]</help>
<valueHelp>
<format>u32:1-20000</format>
- <description>Re-use time in seconds</description>
+ <description>Re-use penalty points</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-20000"/>
@@ -581,7 +581,7 @@
<help>When to start suppressing a route [REQUIRED]</help>
<valueHelp>
<format>u32:1-20000</format>
- <description>Start-suppress-time</description>
+ <description>Start-suppress penalty points</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-20000"/>
diff --git a/interface-definitions/protocols-ospf.xml.in b/interface-definitions/protocols-ospf.xml.in
new file mode 100644
index 000000000..4d5b84be0
--- /dev/null
+++ b/interface-definitions/protocols-ospf.xml.in
@@ -0,0 +1,840 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Protocol OSPF configuration -->
+<interfaceDefinition>
+ <node name="protocols">
+ <children>
+ <node name="nospf" owner="${vyos_conf_scripts_dir}/protocols_ospf.py">
+ <properties>
+ <help>Open Shortest Path First protocol (OSPF) parameters</help>
+ <priority>620</priority>
+ </properties>
+ <children>
+ <tagNode name="access-list">
+ <properties>
+ <help>Access list to filter networks in routing updates</help>
+ <valueHelp>
+ <format>u32</format>
+ <description>Access-list number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="export">
+ <properties>
+ <help>Filter for outgoing routing update [REQUIRED]</help>
+ <completionHelp>
+ <list>bgp connected kernel rip static</list>
+ </completionHelp>
+ <valueHelp>
+ <format>bgp</format>
+ <description>Filter BGP routes</description>
+ </valueHelp>
+ <valueHelp>
+ <format>connected</format>
+ <description>Filter connected routes</description>
+ </valueHelp>
+ <valueHelp>
+ <format>kernel</format>
+ <description>Filter Kernel routes</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rip</format>
+ <description>Filter RIP routes</description>
+ </valueHelp>
+ <valueHelp>
+ <format>static</format>
+ <description>Filter static routes</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(bgp|connected|kernel|rip|static)$</regex>
+ </constraint>
+ <constraintErrorMessage>Must be bgp, connected, kernel, rip, or static</constraintErrorMessage>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <tagNode name="area">
+ <properties>
+ <help>OSPF Area</help>
+ <valueHelp>
+ <format>u32</format>
+ <description>OSPF area in decimal notation</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>OSPF area in dotted decimal notation</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ <validator name="ip-address"/>
+ </constraint>
+ </properties>
+ <children>
+ <node name="area-type">
+ <properties>
+ <help>Area type</help>
+ </properties>
+ <children>
+ <leafNode name="normal">
+ <properties>
+ <help>Normal OSPF area</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="nssa">
+ <properties>
+ <help>Nssa OSPF area</help>
+ </properties>
+ <children>
+ <leafNode name="default-cost">
+ <properties>
+ <help>Summary-default cost of nssa area</help>
+ <valueHelp>
+ <format>u32:0-16777215</format>
+ <description>Summary default cost</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-16777215"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="no-summary">
+ <properties>
+ <help>Do not inject inter-area routes into stub</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="translate">
+ <properties>
+ <help>Nssa-abr</help>
+ <completionHelp>
+ <list>always candidate never</list>
+ </completionHelp>
+ <valueHelp>
+ <format>always</format>
+ <description>NSSA-ABR to always translate</description>
+ </valueHelp>
+ <valueHelp>
+ <format>candidate</format>
+ <description>NSSA-ABR for translate election (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>never</format>
+ <description>NSSA-ABR to never translate</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(always|candidate|never)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="stub">
+ <properties>
+ <help>Stub OSPF area</help>
+ </properties>
+ <children>
+ <leafNode name="default-cost">
+ <properties>
+ <help>Summary-default cost of nssa area</help>
+ <valueHelp>
+ <format>u32:0-16777215</format>
+ <description>Summary default cost</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-16777215"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="no-summary">
+ <properties>
+ <help>Do not inject inter-area routes into stub</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <leafNode name="authentication">
+ <properties>
+ <help>OSPF area authentication type</help>
+ <completionHelp>
+ <list>plaintext-password md5</list>
+ </completionHelp>
+ <valueHelp>
+ <format>plaintext-password</format>
+ <description>Use plain-text authentication</description>
+ </valueHelp>
+ <valueHelp>
+ <format>md5</format>
+ <description>Use md5 authentication</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(plaintext-password|md5)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="network">
+ <properties>
+ <help>OSPF network [REQUIRED]</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>OSPF network [REQUIRED]</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <tagNode name="range">
+ <properties>
+ <help>Summarize routes matching prefix (border routers only)</help>
+ </properties>
+ <children>
+ <leafNode name="cost">
+ <properties>
+ <help>Metric for this range</help>
+ <valueHelp>
+ <format>u32:0-16777215</format>
+ <description>Metric for this range</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-16777215"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="not-advertise">
+ <properties>
+ <help>Do not advertise this range</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="substitute">
+ <properties>
+ <help>Announce area range as another prefix</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>Announce area range as another prefix</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <leafNode name="shortcut">
+ <properties>
+ <help>Area shortcut mode</help>
+ <completionHelp>
+ <list>default disable enable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>default</format>
+ <description>Set default</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable shortcutting mode</description>
+ </valueHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable shortcutting mode</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(default|disable|enable)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="virtual-link">
+ <properties>
+ <help>Virtual link</help>
+ </properties>
+ <children>
+ <node name="authentication">
+ <properties>
+ <help>Authentication</help>
+ </properties>
+ <children>
+ <node name="md5">
+ <properties>
+ <help>MD5 key id</help>
+ </properties>
+ <children>
+ <tagNode name="key-id">
+ <properties>
+ <help>MD5 key id</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>MD5 key id</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="md5-key">
+ <properties>
+ <help>MD5 authentication type</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>MD5 Key (16 characters or less)</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <leafNode name="plaintext-password">
+ <properties>
+ <help>Plain text password</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Plain text password (8 characters or less)</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="dead-interval">
+ <properties>
+ <help>Interval after which a neighbor is declared dead</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Neighbor dead interval (seconds)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-interval">
+ <properties>
+ <help>Interval between hello packets</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Hello interval (seconds)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="retransmit-interval">
+ <properties>
+ <help>Interval between retransmitting lost link state advertisements</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Retransmit interval (seconds)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="transmit-delay">
+ <properties>
+ <help>Link state transmit delay</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Link state transmit delay (seconds)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ <node name="auto-cost">
+ <properties>
+ <help>Calculate OSPF interface cost according to bandwidth</help>
+ </properties>
+ <children>
+ <leafNode name="reference-bandwidth">
+ <properties>
+ <help>Reference bandwidth method to assign OSPF cost</help>
+ <valueHelp>
+ <format>u32:1-4294967</format>
+ <description>Reference bandwidth cost in Mbits/sec (default 100)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="default-information">
+ <properties>
+ <help>Control distribution of default information</help>
+ </properties>
+ <children>
+ <node name="originate">
+ <properties>
+ <help>Distribute a default route</help>
+ </properties>
+ <children>
+ <leafNode name="always">
+ <properties>
+ <help>Always advertise default route</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="metric">
+ <properties>
+ <help>OSPF default metric</help>
+ <valueHelp>
+ <format>u32:0-16777214</format>
+ <description>Default metric</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-16777214"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="metric-type">
+ <properties>
+ <help>OSPF metric type for default routes</help>
+ <valueHelp>
+ <format>u32:1-2</format>
+ <description>Metric type for default routes (default 2)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-2"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="route-map">
+ <properties>
+ <help>Route map reference</help>
+ <completionHelp>
+ <path>policy route-map</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <leafNode name="default-metric">
+ <properties>
+ <help>Metric of redistributed routes</help>
+ <valueHelp>
+ <format>u32:0-16777214</format>
+ <description>Metric of redistributed routes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-16777214"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="distance">
+ <properties>
+ <help>Administrative distance</help>
+ </properties>
+ <children>
+ <leafNode name="global">
+ <properties>
+ <help>OSPF administrative distance</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Administrative distance</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="ospf">
+ <properties>
+ <help>OSPF administrative distance</help>
+ </properties>
+ <children>
+ <leafNode name="external">
+ <properties>
+ <help>Distance for external routes</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Distance for external routes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="inter-area">
+ <properties>
+ <help>Distance for inter-area routes</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Distance for inter-area routes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="intra-area">
+ <properties>
+ <help>Distance for intra-area routes</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Distance for intra-area routes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="log-adjacency-changes">
+ <properties>
+ <help>Log changes in adjacency state</help>
+ </properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Log all state changes</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="max-metric">
+ <properties>
+ <help>OSPF maximum and infinite-distance metric</help>
+ </properties>
+ <children>
+ <node name="router-lsa">
+ <properties>
+ <help>Advertise own Router-LSA with infinite distance (stub router)</help>
+ </properties>
+ <children>
+ <leafNode name="administrative">
+ <properties>
+ <help>Administratively apply, for an indefinite period</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="on-shutdown">
+ <properties>
+ <help>Advertise stub-router prior to full shutdown of OSPF</help>
+ <valueHelp>
+ <format>u32:5-86400</format>
+ <description>Time (seconds) to advertise self as stub-router</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 5-86400"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="on-startup">
+ <properties>
+ <help>Automatically advertise stub Router-LSA on startup of OSPF</help>
+ <valueHelp>
+ <format>u32:5-86400</format>
+ <description>Time (seconds) to advertise self as stub-router</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 5-86400"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="mpls-te">
+ <properties>
+ <help>MultiProtocol Label Switching-Traffic Engineering (MPLS-TE) parameters</help>
+ </properties>
+ <children>
+ <leafNode name="enable">
+ <properties>
+ <help>Enable MPLS-TE functionality</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="router-address">
+ <properties>
+ <help>Stable IP address of the advertising router</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Stable IP address of the advertising router</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="neighbor">
+ <properties>
+ <help>Neighbor IP address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Neighbor IP address</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="poll-interval">
+ <properties>
+ <help>Dead neighbor polling interval</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Seconds between dead neighbor polling interval (default 60)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="priority">
+ <properties>
+ <help>Neighbor priority in seconds</help>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>Neighbor priority (default 0)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="parameters">
+ <properties>
+ <help>OSPF specific parameters</help>
+ </properties>
+ <children>
+ <leafNode name="abr-type">
+ <properties>
+ <help>OSPF ABR type</help>
+ <completionHelp>
+ <list>cisco ibm shortcut standard</list>
+ </completionHelp>
+ <valueHelp>
+ <format>cisco</format>
+ <description>Cisco ABR type (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ibm</format>
+ <description>Ibm ABR type</description>
+ </valueHelp>
+ <valueHelp>
+ <format>shortcut</format>
+ <description>Shortcut ABR type</description>
+ </valueHelp>
+ <valueHelp>
+ <format>standard</format>
+ <description>Standard ABR type</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(cisco|ibm|shortcut|standard)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="opaque-lsa">
+ <properties>
+ <help>Enable the Opaque-LSA capability (rfc2370)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="rfc1583-compatibility">
+ <properties>
+ <help>Enable rfc1583 criteria for handling AS external routes</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="router-id">
+ <properties>
+ <help>Override the default router identifier</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Override the default router identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="passive-interface">
+ <properties>
+ <help>Suppress routing updates on an interface</help>
+ <valueHelp>
+ <format>&lt;interface&gt;</format>
+ <description>Interface to be passive (i.e. suppress routing updates)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>default</format>
+ <description>Default to suppress routing updates on all interfaces</description>
+ </valueHelp>
+ <completionHelp>
+ <list>default</list>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="passive-interface-exclude">
+ <properties>
+ <help>Interface to exclude when using 'passive-interface default'</help>
+ <valueHelp>
+ <format>&lt;interface&gt;</format>
+ <description>Interface to be passive (i.e. suppress routing updates)</description>
+ </valueHelp>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ <node name="redistribute">
+ <properties>
+ <help>Redistribute information from another routing protocol</help>
+ </properties>
+ <children>
+ <node name="bgp">
+ <properties>
+ <help>Redistribute BGP routes</help>
+ </properties>
+ <children>
+ #include <include/ospf-metric.xml.i>
+ </children>
+ </node>
+ <node name="connected">
+ <properties>
+ <help>Redistribute connected routes</help>
+ </properties>
+ <children>
+ #include <include/ospf-metric.xml.i>
+ </children>
+ </node>
+ <node name="kernel">
+ <properties>
+ <help>Redistribute kernel routes</help>
+ </properties>
+ <children>
+ #include <include/ospf-metric.xml.i>
+ </children>
+ </node>
+ <node name="rip">
+ <properties>
+ <help>Redistribute rip routes</help>
+ </properties>
+ <children>
+ #include <include/ospf-metric.xml.i>
+ </children>
+ </node>
+ <node name="static">
+ <properties>
+ <help>Redistribute static routes</help>
+ </properties>
+ <children>
+ #include <include/ospf-metric.xml.i>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="refresh">
+ <properties>
+ <help>Adjust refresh parameters</help>
+ </properties>
+ <children>
+ <leafNode name="timers">
+ <properties>
+ <help>Refresh timer</help>
+ <valueHelp>
+ <format>u32:10-1800</format>
+ <description>Timer value in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 10-1800"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="route-map">
+ <properties>
+ <help>Filter routes installed in local route map</help>
+ <completionHelp>
+ <path>policy route-map</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <node name="timers">
+ <properties>
+ <help>Adjust routing timers</help>
+ </properties>
+ <children>
+ <node name="throttle">
+ <properties>
+ <help>Throttling adaptive timers</help>
+ </properties>
+ <children>
+ <node name="spf">
+ <properties>
+ <help>OSPF SPF timers</help>
+ </properties>
+ <children>
+ <leafNode name="delay">
+ <properties>
+ <help>Delay (msec) from first change received till SPF calculation</help>
+ <valueHelp>
+ <format>u32:0-600000</format>
+ <description>Delay in msec (default 200)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-600000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="initial-holdtime">
+ <properties>
+ <help>Initial hold time(msec) between consecutive SPF calculations</help>
+ <valueHelp>
+ <format>u32:0-600000</format>
+ <description>Initial hold time in msec (default 1000)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-600000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="max-holdtime">
+ <properties>
+ <help>Maximum hold time (msec)</help>
+ <valueHelp>
+ <format>u32:0-600000</format>
+ <description>Max hold time in msec (default 10000)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-600000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in
index 06923971d..81c89d94b 100644
--- a/interface-definitions/vrf.xml.in
+++ b/interface-definitions/vrf.xml.in
@@ -40,6 +40,7 @@
</properties>
</leafNode>
#include <include/interface-description.xml.i>
+ #include <include/interface-disable.xml.i>
</children>
</tagNode>
</children>
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index e5e758a8b..5acb1fdfe 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -126,14 +126,14 @@ def leaf_node_changed(conf, path):
return None
-def node_changed(conf, path):
+def node_changed(conf, path, key_mangling=None):
"""
Check if a leaf node was altered. If it has been altered - values has been
changed, or it was added/removed, we will return the old value. If nothing
has been changed, None is returned
"""
from vyos.configdiff import get_config_diff, Diff
- D = get_config_diff(conf, key_mangling=('-', '_'))
+ D = get_config_diff(conf, key_mangling)
D.set_level(conf.get_level())
# get_child_nodes() will return dict_keys(), mangle this into a list with PEP448
keys = D.get_child_nodes_diff(path, expand_nodes=Diff.DELETE)['delete'].keys()
@@ -272,9 +272,9 @@ def has_vlan_subinterface_configured(conf, intf):
old_level = conf.get_level()
conf.set_level([])
- intfpath = 'interfaces ' + Section.get_config_path(intf)
- if ( conf.exists(f'{intfpath} vif') or
- conf.exists(f'{intfpath} vif-s')):
+ intfpath = ['interfaces', Section.section(intf), intf]
+ if ( conf.exists(intfpath + ['vif']) or
+ conf.exists(intfpath + ['vif-s'])):
ret = True
conf.set_level(old_level)
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index 76520f2ba..1bd617a05 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -22,6 +22,7 @@ from vyos.validate import assert_positive
from vyos.util import cmd
from vyos.util import dict_search
from vyos.configdict import get_vlan_ids
+from vyos.configdict import list_diff
@Interface.register
class BridgeIf(Interface):
@@ -274,20 +275,36 @@ class BridgeIf(Interface):
for member in (tmp or []):
if member in interfaces():
self.del_port(member)
- vlan_filter = 0
- vlan_del = set()
- vlan_add = set()
+ # enable/disable Vlan Filter
+ vlan_filter = '1' if 'enable_vlan' in config else '0'
+ self.set_vlan_filter(vlan_filter)
+
+ if int(vlan_filter):
+ add_vlan = []
+ cur_vlan_ids = get_vlan_ids(ifname)
+
+ tmp = dict_search('vif', config)
+ if tmp:
+ for vif, vif_config in tmp.items():
+ add_vlan.append(vif)
+
+ # Remove redundant VLANs from the system
+ for vlan in list_diff(cur_vlan_ids, add_vlan):
+ cmd = f'bridge vlan del dev {ifname} vid {vlan} self'
+ self._cmd(cmd)
+
+ for vlan in add_vlan:
+ cmd = f'bridge vlan add dev {ifname} vid {vlan} self'
+ self._cmd(cmd)
+
+ # VLAN of bridge parent interface is always 1
+ # VLAN 1 is the default VLAN for all unlabeled packets
+ cmd = f'bridge vlan add dev {ifname} vid 1 pvid untagged self'
+ self._cmd(cmd)
tmp = dict_search('member.interface', config)
if tmp:
- if self.get_vlan_filter():
- bridge_vlan_ids = get_vlan_ids(ifname)
- # Delete VLAN ID for the bridge
- if 1 in bridge_vlan_ids:
- bridge_vlan_ids.remove(1)
- for vlan in bridge_vlan_ids:
- vlan_del.add(str(vlan))
for interface, interface_config in tmp.items():
# if interface does yet not exist bail out early and
@@ -315,63 +332,40 @@ class BridgeIf(Interface):
value = interface_config.get('priority')
lower.set_path_priority(value)
- tmp = dict_search('native_vlan_removed', interface_config)
-
- for vlan_id in (tmp or []):
- cmd = f'bridge vlan del dev {interface} vid {vlan_id}'
- self._cmd(cmd)
- cmd = f'bridge vlan add dev {interface} vid 1 pvid untagged master'
- self._cmd(cmd)
- vlan_del.add(vlan_id)
- vlan_add.add(1)
-
- tmp = dict_search('allowed_vlan_removed', interface_config)
-
-
- for vlan_id in (tmp or []):
- cmd = f'bridge vlan del dev {interface} vid {vlan_id}'
- self._cmd(cmd)
- vlan_del.add(vlan_id)
-
- if 'native_vlan' in interface_config:
- vlan_filter = 1
- cmd = f'bridge vlan del dev {interface} vid 1'
- self._cmd(cmd)
- vlan_id = interface_config['native_vlan']
- if int(vlan_id) != 1:
- if 1 in vlan_add:
- vlan_add.remove(1)
- vlan_del.add(1)
- cmd = f'bridge vlan add dev {interface} vid {vlan_id} pvid untagged master'
- self._cmd(cmd)
- vlan_add.add(vlan_id)
- if vlan_id in vlan_del:
- vlan_del.remove(vlan_id)
-
- if 'allowed_vlan' in interface_config:
- vlan_filter = 1
- if 'native_vlan' not in interface_config:
- cmd = f'bridge vlan del dev {interface} vid 1'
+ if int(vlan_filter):
+ add_vlan = []
+ native_vlan_id = None
+ allowed_vlan_ids= []
+ cur_vlan_ids = get_vlan_ids(interface)
+
+ if 'native_vlan' in interface_config:
+ vlan_id = interface_config['native_vlan']
+ add_vlan.append(vlan_id)
+ native_vlan_id = vlan_id
+
+ if 'allowed_vlan' in interface_config:
+ for vlan in interface_config['allowed_vlan']:
+ vlan_range = vlan.split('-')
+ if len(vlan_range) == 2:
+ for vlan_add in range(int(vlan_range[0]),int(vlan_range[1]) + 1):
+ add_vlan.append(str(vlan_add))
+ allowed_vlan_ids.append(str(vlan_add))
+ else:
+ add_vlan.append(vlan)
+ allowed_vlan_ids.append(vlan)
+
+ # Remove redundant VLANs from the system
+ for vlan in list_diff(cur_vlan_ids, add_vlan):
+ cmd = f'bridge vlan del dev {interface} vid {vlan} master'
self._cmd(cmd)
- vlan_del.add(1)
- for vlan in interface_config['allowed_vlan']:
+
+ for vlan in allowed_vlan_ids:
cmd = f'bridge vlan add dev {interface} vid {vlan} master'
self._cmd(cmd)
- vlan_add.add(vlan)
- if vlan in vlan_del:
- vlan_del.remove(vlan)
-
- for vlan in vlan_del:
- cmd = f'bridge vlan del dev {ifname} vid {vlan} self'
- self._cmd(cmd)
-
- for vlan in vlan_add:
- cmd = f'bridge vlan add dev {ifname} vid {vlan} self'
- self._cmd(cmd)
-
- # enable/disable Vlan Filter
- self.set_vlan_filter(vlan_filter)
-
+ # Setting native VLAN to system
+ if native_vlan_id:
+ cmd = f'bridge vlan add dev {interface} vid {native_vlan_id} pvid untagged master'
+ self._cmd(cmd)
# 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()
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 1561d340e..3b92ce463 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -900,50 +900,43 @@ class Interface(Control):
if 'priority' in bridge_config:
self.set_path_cost(bridge_config['priority'])
- vlan_filter = 0
- vlan_add = set()
-
- del_ifname_vlan_ids = get_vlan_ids(ifname)
bridge_vlan_filter = Section.klass(bridge)(bridge, create=True).get_vlan_filter()
- if bridge_vlan_filter:
- if 1 in del_ifname_vlan_ids:
- del_ifname_vlan_ids.remove(1)
- vlan_filter = 1
-
- for vlan in del_ifname_vlan_ids:
- cmd = f'bridge vlan del dev {ifname} vid {vlan}'
- self._cmd(cmd)
-
- if 'native_vlan' in bridge_config:
- vlan_filter = 1
- cmd = f'bridge vlan del dev {self.ifname} vid 1'
- self._cmd(cmd)
- vlan_id = bridge_config['native_vlan']
- cmd = f'bridge vlan add dev {self.ifname} vid {vlan_id} pvid untagged master'
- self._cmd(cmd)
- vlan_add.add(vlan_id)
-
- if 'allowed_vlan' in bridge_config:
- vlan_filter = 1
- if 'native_vlan' not in bridge_config:
- cmd = f'bridge vlan del dev {self.ifname} vid 1'
+ if int(bridge_vlan_filter):
+ cur_vlan_ids = get_vlan_ids(ifname)
+ add_vlan = []
+ native_vlan_id = None
+ allowed_vlan_ids= []
+
+ if 'native_vlan' in bridge_config:
+ vlan_id = bridge_config['native_vlan']
+ add_vlan.append(vlan_id)
+ native_vlan_id = vlan_id
+
+ if 'allowed_vlan' in bridge_config:
+ for vlan in bridge_config['allowed_vlan']:
+ vlan_range = vlan.split('-')
+ if len(vlan_range) == 2:
+ for vlan_add in range(int(vlan_range[0]),int(vlan_range[1]) + 1):
+ add_vlan.append(str(vlan_add))
+ allowed_vlan_ids.append(str(vlan_add))
+ else:
+ add_vlan.append(vlan)
+ allowed_vlan_ids.append(vlan)
+
+ # Remove redundant VLANs from the system
+ for vlan in list_diff(cur_vlan_ids, add_vlan):
+ cmd = f'bridge vlan del dev {ifname} vid {vlan} master'
self._cmd(cmd)
- for vlan in bridge_config['allowed_vlan']:
- cmd = f'bridge vlan add dev {self.ifname} vid {vlan} master'
+
+ for vlan in allowed_vlan_ids:
+ cmd = f'bridge vlan add dev {ifname} vid {vlan} master'
self._cmd(cmd)
- vlan_add.add(vlan)
-
- if vlan_filter:
- # Setting VLAN ID for the bridge
- for vlan in vlan_add:
- cmd = f'bridge vlan add dev {bridge} vid {vlan} self'
+ # Setting native VLAN to system
+ if native_vlan_id:
+ cmd = f'bridge vlan add dev {ifname} vid {native_vlan_id} pvid untagged master'
self._cmd(cmd)
- # enable/disable Vlan Filter
- # When the VLAN aware option is not detected, the setting of `bridge` should not be overwritten
- Section.klass(bridge)(bridge, create=True).set_vlan_filter(vlan_filter)
-
def set_dhcp(self, enable):
"""
Enable/Disable DHCP client on a given interface.
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index 7e3f9565a..4320bf8bc 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -55,6 +55,7 @@ class _Tunnel(Interface):
'ttl' : '',
'tos' : '',
'key' : '',
+ 'raw' : '',
}
options = Interface.options + list(default.keys())
diff --git a/python/vyos/template.py b/python/vyos/template.py
index bf087c223..527384d0b 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -149,7 +149,9 @@ def netmask_from_ipv4(address):
Example:
- 172.18.201.10 -> 255.255.255.128
"""
- from netifaces import interfaces, ifaddresses, AF_INET
+ from netifaces import interfaces
+ from netifaces import ifaddresses
+ from netifaces import AF_INET
for interface in interfaces():
tmp = ifaddresses(interface)
if AF_INET in tmp:
@@ -160,6 +162,30 @@ def netmask_from_ipv4(address):
raise ValueError
+@register_filter('is_ip_network')
+def is_ip_network(addr):
+ """ Take IP(v4/v6) address and validate if the passed argument is a network
+ or a host address.
+
+ Example:
+ - 192.0.2.0 -> False
+ - 192.0.2.10/24 -> False
+ - 192.0.2.0/24 -> True
+ - 2001:db8:: -> False
+ - 2001:db8::100 -> False
+ - 2001:db8::/48 -> True
+ - 2001:db8:1000::/64 -> True
+ """
+ try:
+ from ipaddress import ip_network
+ # input variables must contain a / to indicate its CIDR notation
+ if len(addr.split('/')) != 2:
+ raise ValueError()
+ ip_network(addr)
+ return True
+ except:
+ return False
+
@register_filter('network_from_ipv4')
def network_from_ipv4(address):
""" Take IP address and search all attached interface IP addresses for the
@@ -248,6 +274,21 @@ def dec_ip(address, decrement):
from ipaddress import ip_interface
return str(ip_interface(address).ip - int(decrement))
+@register_filter('compare_netmask')
+def compare_netmask(netmask1, netmask2):
+ """
+ Compare two IP netmask if they have the exact same size.
+
+ compare_netmask('10.0.0.0/8', '20.0.0.0/8') -> True
+ compare_netmask('10.0.0.0/8', '20.0.0.0/16') -> False
+ """
+ from ipaddress import ip_network
+ try:
+ return ip_network(netmask1).netmask == ip_network(netmask2).netmask
+ 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
diff --git a/scripts/override-default b/scripts/override-default
new file mode 100755
index 000000000..d91b89426
--- /dev/null
+++ b/scripts/override-default
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+#
+# override-default: preprocessor for XML interface definitions to interpret
+# redundant entries (relative to path) with tag 'defaultValue' as an override
+# directive. Must be called before build-command-templates, as the schema
+# disallows redundancy.
+#
+# Copyright (C) 2021 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/>.
+#
+#
+
+# Use lxml xpath capability to find multiple elements with tag defaultValue
+# relative to path; replace and remove to override the value.
+
+import sys
+import glob
+import logging
+from lxml import etree
+
+debug = False
+
+logger = logging.getLogger(__name__)
+logs_handler = logging.StreamHandler()
+logger.addHandler(logs_handler)
+
+if debug:
+ logger.setLevel(logging.DEBUG)
+else:
+ logger.setLevel(logging.INFO)
+
+def override_element(l: list):
+ """
+ Allow multiple override elements; use the final one (in document order).
+ """
+ if len(l) < 2:
+ logger.debug("passing list of single element to override_element")
+ return
+
+ # assemble list of leafNodes of overriding defaultValues, for later removal
+ parents = []
+ for el in l[1:]:
+ parents.append(el.getparent())
+
+ # replace element with final override
+ l[0].getparent().replace(l[0], l[-1])
+
+ # remove all but overridden element
+ for el in parents:
+ el.getparent().remove(el)
+
+def collect_and_override(dir_name):
+ """
+ Collect elements with defaultValue tag into dictionary indexed by tuple
+ of (name, str(ancestor path)); the second component must be immutable for
+ tuple to act as key, hence str().
+ """
+ for fname in glob.glob(f'{dir_name}/*.xml'):
+ tree = etree.parse(fname)
+ root = tree.getroot()
+ defv = {}
+
+ xpath_str = f'//defaultValue'
+ xp = tree.xpath(xpath_str)
+
+ for element in xp:
+ ap = element.xpath('ancestor::*[@name]')
+ defv.setdefault((ap[-1].get("name"), str(ap[:-1])), []).append(element)
+
+ for k, v in defv.items():
+ if len(v) > 1:
+ logger.debug(f'overridding default in {k[0]}')
+ override_element(v)
+
+ revised_str = etree.tostring(root, encoding='unicode', pretty_print=True)
+
+ with open(f'{fname}', 'w') as f:
+ f.write(revised_str)
+
+def main():
+ if len(sys.argv) < 2:
+ logger.critical('Must specify XML directory!')
+ sys.exit(1)
+
+ dir_name = sys.argv[1]
+
+ collect_and_override(dir_name)
+
+if __name__ == '__main__':
+ main()
diff --git a/smoketest/configs/bgp-ix-router b/smoketest/configs/bgp-ixp
index de6213b50..de6213b50 100644
--- a/smoketest/configs/bgp-ix-router
+++ b/smoketest/configs/bgp-ixp
diff --git a/smoketest/configs/bgp-rpki b/smoketest/configs/bgp-rpki
new file mode 100644
index 000000000..e11ec9e72
--- /dev/null
+++ b/smoketest/configs/bgp-rpki
@@ -0,0 +1,116 @@
+interfaces {
+ ethernet eth0 {
+ address 192.0.2.100/25
+ address 2001:db8::ffff/64
+ }
+ ethernet eth1 {
+ }
+ loopback lo {
+ }
+}
+policy {
+ route-map ebgp-transit-rpki {
+ rule 10 {
+ action deny
+ match {
+ rpki invalid
+ }
+ }
+ rule 20 {
+ action permit
+ match {
+ rpki notfound
+ }
+ set {
+ local-preference 20
+ }
+ }
+ rule 30 {
+ action permit
+ match {
+ rpki valid
+ }
+ set {
+ local-preference 100
+ }
+ }
+ }
+}
+protocols {
+ bgp 64500 {
+ neighbor 1.2.3.4 {
+ address-family {
+ ipv4-unicast {
+ nexthop-self {
+ }
+ route-map {
+ import ebgp-transit-rpki
+ }
+ }
+ }
+ remote-as 10
+ }
+ }
+ rpki {
+ cache routinator {
+ address 192.0.2.10
+ port 3323
+ }
+ }
+ static {
+ route 0.0.0.0/0 {
+ next-hop 192.0.2.1 {
+ }
+ }
+ route6 ::/0 {
+ next-hop 2001:db8::1 {
+ }
+ }
+ }
+}
+service {
+ ssh {
+ }
+}
+system {
+ config-management {
+ commit-revisions 100
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+ plaintext-password ""
+ }
+ }
+ }
+ ntp {
+ server 0.pool.ntp.org {
+ }
+ server 1.pool.ntp.org {
+ }
+ server 2.pool.ntp.org {
+ }
+ }
+ syslog {
+ global {
+ facility all {
+ level info
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ }
+}
+
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@13:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@19:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webgui@1:webproxy@2:zone-policy@1"
+// Release version: 1.3-rolling-202010241631
diff --git a/smoketest/configs/dmz-guest-lan-nat-pppoe-router b/smoketest/configs/dmz-guest-lan-nat-pppoe-router
new file mode 100644
index 000000000..075f18b9e
--- /dev/null
+++ b/smoketest/configs/dmz-guest-lan-nat-pppoe-router
@@ -0,0 +1,1666 @@
+firewall {
+ all-ping enable
+ broadcast-ping disable
+ config-trap disable
+ group {
+ address-group MEDIA-STREAMING-CLIENTS {
+ address 172.16.35.241
+ address 172.16.35.242
+ address 172.16.35.243
+ }
+ address-group DMZ-WEBSERVER {
+ address 172.16.36.10
+ address 172.16.36.40
+ address 172.16.36.20
+ }
+ address-group DMZ-RDP-SERVER {
+ address 172.16.33.40
+ }
+ address-group DOMAIN-CONTROLLER {
+ address 172.16.100.10
+ address 172.16.100.20
+ }
+ address-group AUDIO-STREAM {
+ address 172.16.35.20
+ address 172.16.35.21
+ address 172.16.35.22
+ address 172.16.35.23
+ }
+ ipv6-network-group LOCAL-ADDRESSES {
+ network ff02::/64
+ network fe80::/10
+ }
+ network-group SSH-IN-ALLOW {
+ network 192.0.2.0/24
+ network 10.0.0.0/8
+ network 172.16.0.0/12
+ network 192.168.0.0/16
+ }
+ port-group SMART-TV-PORTS {
+ port 5005-5006
+ port 80
+ port 443
+ port 3722
+ }
+ }
+ ipv6-name ALLOW-ALL-6 {
+ default-action accept
+ }
+ ipv6-name ALLOW-BASIC-6 {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ protocol icmpv6
+ }
+ }
+ ipv6-name ALLOW-ESTABLISHED-6 {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ destination {
+ group {
+ network-group LOCAL-ADDRESSES
+ }
+ }
+ protocol icmpv6
+ source {
+ address fe80::/10
+ }
+ }
+ rule 20 {
+ action accept
+ icmpv6 {
+ type echo-request
+ }
+ protocol icmpv6
+ }
+ rule 21 {
+ action accept
+ icmpv6 {
+ type destination-unreachable
+ }
+ protocol icmpv6
+ }
+ rule 22 {
+ action accept
+ icmpv6 {
+ type packet-too-big
+ }
+ protocol icmpv6
+ }
+ rule 23 {
+ action accept
+ icmpv6 {
+ type time-exceeded
+ }
+ protocol icmpv6
+ }
+ rule 24 {
+ action accept
+ icmpv6 {
+ type parameter-problem
+ }
+ protocol icmpv6
+ }
+ }
+ ipv6-name WAN-LOCAL-6 {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ destination {
+ address ff02::/64
+ }
+ protocol icmpv6
+ source {
+ address fe80::/10
+ }
+ }
+ rule 50 {
+ action accept
+ description DHCPv6
+ destination {
+ address fe80::/10
+ port 546
+ }
+ protocol udp
+ source {
+ address fe80::/10
+ port 547
+ }
+ }
+ }
+ ipv6-receive-redirects disable
+ ipv6-src-route disable
+ ip-src-route disable
+ log-martians enable
+ name DMZ-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name DMZ-LAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ description "NTP and LDAP to AD DC"
+ destination {
+ group {
+ address-group DOMAIN-CONTROLLER
+ }
+ port 123,389,636
+ }
+ protocol tcp_udp
+ }
+ rule 300 {
+ action accept
+ destination {
+ group {
+ address-group DMZ-RDP-SERVER
+ }
+ port 3389
+ }
+ protocol tcp_udp
+ source {
+ address 172.16.36.20
+ }
+ }
+ }
+ name DMZ-LOCAL {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 50 {
+ action accept
+ destination {
+ address 172.16.254.30
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 123 {
+ action accept
+ destination {
+ port 123
+ }
+ protocol udp
+ }
+ }
+ name DMZ-WAN {
+ default-action accept
+ }
+ name GUEST-DMZ {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ destination {
+ port 80,443
+ }
+ protocol tcp
+ }
+ }
+ name GUEST-IOT {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ description "MEDIA-STREAMING-CLIENTS Devices to GUEST"
+ destination {
+ group {
+ address-group MEDIA-STREAMING-CLIENTS
+ }
+ }
+ protocol tcp_udp
+ }
+ rule 110 {
+ action accept
+ description "AUDIO-STREAM Devices to GUEST"
+ destination {
+ group {
+ address-group AUDIO-STREAM
+ }
+ }
+ protocol tcp_udp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 300 {
+ action accept
+ description "BCAST relay"
+ destination {
+ port 1900
+ }
+ protocol udp
+ }
+ }
+ name GUEST-LAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name GUEST-LOCAL {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ description DNS
+ destination {
+ address 172.31.0.254
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 11 {
+ action accept
+ description DHCP
+ destination {
+ port 67
+ }
+ protocol udp
+ }
+ rule 15 {
+ action accept
+ destination {
+ address 172.31.0.254
+ }
+ protocol icmp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 210 {
+ action accept
+ description "AUDIO-STREAM Broadcast"
+ destination {
+ port 1900
+ }
+ protocol udp
+ }
+ }
+ name GUEST-WAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 25 {
+ action accept
+ description SMTP
+ destination {
+ port 25,587
+ }
+ protocol tcp
+ }
+ rule 53 {
+ action accept
+ destination {
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 60 {
+ action accept
+ source {
+ address 172.31.0.200
+ }
+ }
+ rule 80 {
+ action accept
+ source {
+ address 172.31.0.200
+ }
+ }
+ rule 100 {
+ action accept
+ protocol icmp
+ }
+ rule 110 {
+ action accept
+ description POP3
+ destination {
+ port 110,995
+ }
+ protocol tcp
+ }
+ rule 123 {
+ action accept
+ description "NTP Client"
+ destination {
+ port 123
+ }
+ protocol udp
+ }
+ rule 143 {
+ action accept
+ description IMAP
+ destination {
+ port 143,993
+ }
+ protocol tcp
+ }
+ rule 200 {
+ action accept
+ destination {
+ port 80,443
+ }
+ protocol tcp
+ }
+ rule 500 {
+ action accept
+ description "L2TP IPSec"
+ destination {
+ port 500,4500
+ }
+ protocol udp
+ }
+ rule 600 {
+ action accept
+ destination {
+ port 5222-5224
+ }
+ protocol tcp
+ }
+ rule 601 {
+ action accept
+ destination {
+ port 3478-3497,4500,16384-16387,16393-16402
+ }
+ protocol udp
+ }
+ rule 1000 {
+ action accept
+ source {
+ address 172.31.0.184
+ }
+ }
+ }
+ name IOT-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ description "MEDIA-STREAMING-CLIENTS Devices to IOT"
+ protocol tcp_udp
+ source {
+ group {
+ address-group MEDIA-STREAMING-CLIENTS
+ }
+ }
+ }
+ rule 110 {
+ action accept
+ description "AUDIO-STREAM Devices to IOT"
+ protocol tcp_udp
+ source {
+ group {
+ address-group AUDIO-STREAM
+ }
+ }
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 300 {
+ action accept
+ description "BCAST relay"
+ destination {
+ port 1900
+ }
+ protocol udp
+ }
+ }
+ name IOT-LAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ description "AppleTV to LAN"
+ destination {
+ group {
+ port-group SMART-TV-PORTS
+ }
+ }
+ protocol tcp_udp
+ source {
+ group {
+ address-group MEDIA-STREAMING-CLIENTS
+ }
+ }
+ }
+ rule 110 {
+ action accept
+ description "AUDIO-STREAM Devices to LAN"
+ protocol tcp_udp
+ source {
+ group {
+ address-group AUDIO-STREAM
+ }
+ }
+ }
+ }
+ name IOT-LOCAL {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ description DNS
+ destination {
+ address 172.16.254.30
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 11 {
+ action accept
+ description DHCP
+ destination {
+ port 67
+ }
+ protocol udp
+ }
+ rule 15 {
+ action accept
+ destination {
+ address 172.16.35.254
+ }
+ protocol icmp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 201 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 172.16.35.254
+ port 5353
+ }
+ protocol udp
+ }
+ rule 210 {
+ action accept
+ description "AUDIO-STREAM Broadcast"
+ destination {
+ port 1900,1902,6969
+ }
+ protocol udp
+ }
+ }
+ name IOT-WAN {
+ default-action accept
+ }
+ name LAN-DMZ {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 22 {
+ action accept
+ description "SSH into DMZ"
+ destination {
+ port 22
+ }
+ protocol tcp
+ }
+ rule 100 {
+ action accept
+ destination {
+ group {
+ address-group DMZ-WEBSERVER
+ }
+ port 22,80,443
+ }
+ protocol tcp
+ }
+ }
+ name LAN-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name LAN-IOT {
+ default-action accept
+ }
+ name LAN-LOCAL {
+ default-action accept
+ }
+ name LAN-WAN {
+ default-action accept
+ }
+ name LOCAL-DMZ {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name LOCAL-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 5 {
+ action accept
+ protocol icmp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 300 {
+ action accept
+ description "BCAST relay"
+ destination {
+ port 1900
+ }
+ protocol udp
+ }
+ }
+ name LOCAL-IOT {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 5 {
+ action accept
+ protocol icmp
+ }
+ rule 200 {
+ action accept
+ description "MCAST relay"
+ destination {
+ address 224.0.0.251
+ port 5353
+ }
+ protocol udp
+ }
+ rule 300 {
+ action accept
+ description "BCAST relay"
+ destination {
+ port 1900,6969
+ }
+ protocol udp
+ }
+ }
+ name LOCAL-LAN {
+ default-action accept
+ }
+ name LOCAL-WAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 10 {
+ action accept
+ protocol icmp
+ }
+ rule 50 {
+ action accept
+ description DNS
+ destination {
+ port 53
+ }
+ protocol tcp_udp
+ }
+ rule 80 {
+ action accept
+ destination {
+ port 80,443
+ }
+ protocol tcp
+ }
+ rule 123 {
+ action accept
+ description NTP
+ destination {
+ port 123
+ }
+ protocol udp
+ }
+ }
+ name WAN-DMZ {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 100 {
+ action accept
+ destination {
+ address 172.16.36.10
+ port 80,443
+ }
+ protocol tcp
+ }
+ }
+ name WAN-GUEST {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 1000 {
+ action accept
+ destination {
+ address 172.31.0.184
+ }
+ }
+ rule 8000 {
+ action accept
+ destination {
+ address 172.31.0.200
+ port 10000
+ }
+ protocol udp
+ }
+ }
+ name WAN-IOT {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ }
+ name WAN-LAN {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 1000 {
+ action accept
+ destination {
+ address 172.16.33.40
+ port 3389
+ }
+ protocol tcp
+ source {
+ group {
+ network-group SSH-IN-ALLOW
+ }
+ }
+ }
+ }
+ name WAN-LOCAL {
+ default-action drop
+ enable-default-log
+ rule 1 {
+ action accept
+ state {
+ established enable
+ related enable
+ }
+ }
+ rule 2 {
+ action drop
+ log enable
+ state {
+ invalid enable
+ }
+ }
+ rule 22 {
+ action accept
+ destination {
+ port 22
+ }
+ protocol tcp
+ source {
+ group {
+ network-group SSH-IN-ALLOW
+ }
+ }
+ }
+ }
+ options {
+ interface pppoe0 {
+ adjust-mss 1452
+ adjust-mss6 1432
+ }
+ }
+ receive-redirects disable
+ send-redirects enable
+ source-validation disable
+ syn-cookies enable
+ twa-hazards-protection disable
+}
+interfaces {
+ dummy dum0 {
+ address 172.16.254.30/32
+ }
+ ethernet eth0 {
+ duplex auto
+ speed auto
+ vif 5 {
+ address 172.16.37.254/24
+ }
+ vif 10 {
+ address 172.16.33.254/24
+ }
+ vif 20 {
+ address 172.31.0.254/24
+ }
+ vif 35 {
+ address 172.16.35.254/24
+ }
+ vif 50 {
+ address 172.16.36.254/24
+ }
+ vif 100 {
+ address 172.16.100.254/24
+ }
+ vif 201 {
+ address 172.18.201.254/24
+ }
+ vif 202 {
+ address 172.18.202.254/24
+ }
+ vif 203 {
+ address 172.18.203.254/24
+ }
+ vif 204 {
+ address 172.18.204.254/24
+ }
+ }
+ ethernet eth1 {
+ vif 7 {
+ description FTTH-PPPoE
+ }
+ }
+ loopback lo {
+ address 172.16.254.30/32
+ }
+ pppoe pppoe0 {
+ authentication {
+ password vyos
+ user vyos
+ }
+ default-route auto
+ description "FTTH 100/50MBit"
+ dhcpv6-options {
+ pd 0 {
+ interface eth0.10 {
+ address 1
+ sla-id 10
+ }
+ interface eth0.20 {
+ address 1
+ sla-id 20
+ }
+ length 56
+ }
+ }
+ ipv6 {
+ address {
+ autoconf
+ }
+ }
+ mtu 1492
+ no-peer-dns
+ source-interface eth1.7
+ }
+}
+nat {
+ destination {
+ rule 100 {
+ description HTTP(S)
+ destination {
+ port 80,443
+ }
+ inbound-interface pppoe0
+ log
+ protocol tcp
+ translation {
+ address 172.16.36.10
+ }
+ }
+ rule 1000 {
+ destination {
+ port 3389
+ }
+ disable
+ inbound-interface pppoe0
+ protocol tcp
+ translation {
+ address 172.16.33.40
+ }
+ }
+ rule 8000 {
+ destination {
+ port 10000
+ }
+ inbound-interface pppoe0
+ log
+ protocol udp
+ translation {
+ address 172.31.0.200
+ }
+ }
+ }
+ source {
+ rule 100 {
+ log
+ outbound-interface pppoe0
+ source {
+ address 172.16.32.0/19
+ }
+ translation {
+ address masquerade
+ }
+ }
+ rule 200 {
+ outbound-interface pppoe0
+ source {
+ address 172.16.100.0/24
+ }
+ translation {
+ address masquerade
+ }
+ }
+ rule 300 {
+ outbound-interface pppoe0
+ source {
+ address 172.31.0.0/24
+ }
+ translation {
+ address masquerade
+ }
+ }
+ rule 400 {
+ outbound-interface pppoe0
+ source {
+ address 172.18.200.0/21
+ }
+ translation {
+ address masquerade
+ }
+ }
+ }
+}
+protocols {
+ static {
+ interface-route6 2000::/3 {
+ next-hop-interface pppoe0 {
+ }
+ }
+ route 10.0.0.0/8 {
+ blackhole {
+ distance 254
+ }
+ }
+ route 169.254.0.0/16 {
+ blackhole {
+ distance 254
+ }
+ }
+ route 172.16.0.0/12 {
+ blackhole {
+ distance 254
+ }
+ }
+ route 192.168.0.0/16 {
+ blackhole {
+ distance 254
+ }
+ }
+ }
+}
+service {
+ dhcp-server {
+ shared-network-name BACKBONE {
+ authoritative
+ subnet 172.16.37.0/24 {
+ default-router 172.16.37.254
+ dns-server 172.16.254.30
+ domain-name vyos.net
+ domain-search vyos.net
+ lease 86400
+ ntp-server 172.16.254.30
+ range 0 {
+ start 172.16.37.120
+ stop 172.16.37.149
+ }
+ static-mapping AP1.wue3 {
+ ip-address 172.16.37.231
+ mac-address 18:e8:29:6c:c3:a5
+ }
+ }
+ }
+ shared-network-name GUEST {
+ authoritative
+ subnet 172.31.0.0/24 {
+ default-router 172.31.0.254
+ dns-server 172.31.0.254
+ domain-name vyos.net
+ domain-search vyos.net
+ lease 86400
+ range 0 {
+ start 172.31.0.100
+ stop 172.31.0.199
+ }
+ static-mapping host01 {
+ ip-address 172.31.0.200
+ mac-address 00:50:00:00:00:01
+ }
+ static-mapping host02 {
+ ip-address 172.31.0.184
+ mac-address 00:50:00:00:00:02
+ }
+ }
+ }
+ shared-network-name IOT {
+ authoritative
+ subnet 172.16.35.0/24 {
+ default-router 172.16.35.254
+ dns-server 172.16.254.30
+ domain-name vyos.net
+ domain-search vyos.net
+ lease 86400
+ ntp-server 172.16.254.30
+ range 0 {
+ start 172.16.35.101
+ stop 172.16.35.149
+ }
+ }
+ }
+ shared-network-name LAN {
+ authoritative
+ subnet 172.16.33.0/24 {
+ default-router 172.16.33.254
+ dns-server 172.16.254.30
+ domain-name vyos.net
+ domain-search vyos.net
+ lease 86400
+ ntp-server 172.16.254.30
+ range 0 {
+ start 172.16.33.100
+ stop 172.16.33.189
+ }
+ }
+ }
+ }
+ dns {
+ forwarding {
+ allow-from 172.16.0.0/12
+ cache-size 0
+ domain 16.172.in-addr.arpa {
+ addnta
+ recursion-desired
+ server 172.16.100.10
+ server 172.16.100.20
+ server 172.16.110.30
+ }
+ domain 18.172.in-addr.arpa {
+ addnta
+ recursion-desired
+ server 172.16.100.10
+ server 172.16.100.20
+ server 172.16.110.30
+ }
+ domain vyos.net {
+ addnta
+ recursion-desired
+ server 172.16.100.20
+ server 172.16.100.10
+ server 172.16.110.30
+ }
+ ignore-hosts-file
+ listen-address 172.16.254.30
+ listen-address 172.31.0.254
+ negative-ttl 60
+ }
+ }
+ lldp {
+ legacy-protocols {
+ cdp
+ }
+ snmp {
+ enable
+ }
+ }
+ mdns {
+ repeater {
+ interface eth0.35
+ interface eth0.10
+ }
+ }
+ router-advert {
+ interface eth0.10 {
+ prefix ::/64 {
+ preferred-lifetime 2700
+ valid-lifetime 5400
+ }
+ }
+ interface eth0.20 {
+ prefix ::/64 {
+ preferred-lifetime 2700
+ valid-lifetime 5400
+ }
+ }
+ }
+ snmp {
+ community fooBar {
+ authorization ro
+ network 172.16.100.0/24
+ }
+ contact "VyOS maintainers and contributors <maintainers@vyos.io>"
+ listen-address 172.16.254.30 {
+ port 161
+ }
+ location "The Internet"
+ }
+ ssh {
+ disable-host-validation
+ port 22
+ }
+}
+system {
+ config-management {
+ commit-archive {
+ location tftp://172.16.20.200/CONFIGS/vyos
+ }
+ commit-revisions 200
+ }
+ conntrack {
+ expect-table-size 2048
+ hash-size 32768
+ modules {
+ sip {
+ disable
+ }
+ }
+ table-size 262144
+ timeout {
+ icmp 30
+ other 600
+ udp {
+ other 300
+ stream 300
+ }
+ }
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ domain-name vyos.net
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+ plaintext-password ""
+ }
+ }
+ }
+ name-server 172.16.254.30
+ ntp {
+ allow-clients {
+ address 172.16.0.0/12
+ }
+ server 0.pool.ntp.org {
+ }
+ server 1.pool.ntp.org {
+ }
+ server 2.pool.ntp.org {
+ }
+ }
+ option {
+ ctrl-alt-delete ignore
+ reboot-on-panic
+ startup-beep
+ }
+ syslog {
+ global {
+ facility all {
+ level debug
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ host 172.16.100.1 {
+ facility all {
+ level warning
+ }
+ }
+ }
+ time-zone Europe/Berlin
+}
+traffic-policy {
+ shaper QoS {
+ bandwidth 50mbit
+ default {
+ bandwidth 100%
+ burst 15k
+ queue-limit 1000
+ queue-type fq-codel
+ }
+ }
+}
+zone-policy {
+ zone DMZ {
+ default-action drop
+ from GUEST {
+ firewall {
+ name GUEST-DMZ
+ }
+ }
+ from LAN {
+ firewall {
+ name LAN-DMZ
+ }
+ }
+ from LOCAL {
+ firewall {
+ name LOCAL-DMZ
+ }
+ }
+ from WAN {
+ firewall {
+ name WAN-DMZ
+ }
+ }
+ interface eth0.50
+ }
+ zone GUEST {
+ default-action drop
+ from DMZ {
+ firewall {
+ name DMZ-GUEST
+ }
+ }
+ from IOT {
+ firewall {
+ name IOT-GUEST
+ }
+ }
+ from LAN {
+ firewall {
+ name LAN-GUEST
+ }
+ }
+ from LOCAL {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LOCAL-GUEST
+ }
+ }
+ from WAN {
+ firewall {
+ ipv6-name ALLOW-ESTABLISHED-6
+ name WAN-GUEST
+ }
+ }
+ interface eth0.20
+ }
+ zone IOT {
+ default-action drop
+ from GUEST {
+ firewall {
+ name GUEST-IOT
+ }
+ }
+ from LAN {
+ firewall {
+ name LAN-IOT
+ }
+ }
+ from LOCAL {
+ firewall {
+ name LOCAL-IOT
+ }
+ }
+ from WAN {
+ firewall {
+ name WAN-IOT
+ }
+ }
+ interface eth0.35
+ }
+ zone LAN {
+ default-action drop
+ from DMZ {
+ firewall {
+ name DMZ-LAN
+ }
+ }
+ from GUEST {
+ firewall {
+ name GUEST-LAN
+ }
+ }
+ from IOT {
+ firewall {
+ name IOT-LAN
+ }
+ }
+ from LOCAL {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LOCAL-LAN
+ }
+ }
+ from WAN {
+ firewall {
+ ipv6-name ALLOW-ESTABLISHED-6
+ name WAN-LAN
+ }
+ }
+ interface eth0.5
+ interface eth0.10
+ interface eth0.100
+ interface eth0.201
+ interface eth0.202
+ interface eth0.203
+ interface eth0.204
+ }
+ zone LOCAL {
+ default-action drop
+ from DMZ {
+ firewall {
+ name DMZ-LOCAL
+ }
+ }
+ from GUEST {
+ firewall {
+ ipv6-name ALLOW-ESTABLISHED-6
+ name GUEST-LOCAL
+ }
+ }
+ from IOT {
+ firewall {
+ name IOT-LOCAL
+ }
+ }
+ from LAN {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LAN-LOCAL
+ }
+ }
+ from WAN {
+ firewall {
+ ipv6-name WAN-LOCAL-6
+ name WAN-LOCAL
+ }
+ }
+ local-zone
+ }
+ zone WAN {
+ default-action drop
+ from DMZ {
+ firewall {
+ name DMZ-WAN
+ }
+ }
+ from GUEST {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name GUEST-WAN
+ }
+ }
+ from IOT {
+ firewall {
+ name IOT-WAN
+ }
+ }
+ from LAN {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LAN-WAN
+ }
+ }
+ from LOCAL {
+ firewall {
+ ipv6-name ALLOW-ALL-6
+ name LOCAL-WAN
+ }
+ }
+ interface pppoe0
+ }
+}
+
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
+// Release version: 1.3-beta-202101091250
+
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 8a09dd96f..6a68bcc26 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -18,9 +18,10 @@ import json
from binascii import hexlify
-from netifaces import ifaddresses
from netifaces import AF_INET
from netifaces import AF_INET6
+from netifaces import ifaddresses
+from netifaces import interfaces
from vyos.configsession import ConfigSession
from vyos.ifconfig import Interface
@@ -81,21 +82,22 @@ class BasicInterfaceTest:
self.session.set(['interfaces', section, span])
def tearDown(self):
- # Ethernet is handled in its derived class
- if 'ethernet' not in self._base_path:
- self.session.delete(self._base_path)
-
# Tear down mirror interfaces for SPAN (Switch Port Analyzer)
for span in self._mirror_interfaces:
section = Section.section(span)
self.session.delete(['interfaces', section, span])
+ self.session.delete(self._base_path)
self.session.commit()
del self.session
+ # Verify that no previously interface remained on the system
+ for intf in self._interfaces:
+ self.assertNotIn(intf, interfaces())
+
def test_span_mirror(self):
if not self._mirror_interfaces:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
# Check the two-way mirror rules of ingress and egress
for mirror in self._mirror_interfaces:
@@ -111,6 +113,19 @@ class BasicInterfaceTest:
self.assertTrue(is_mirrored_to(interface, mirror, 'ffff'))
self.assertTrue(is_mirrored_to(interface, mirror, '1'))
+ def test_interface_disable(self):
+ # Check if description can be added to interface and
+ # can be read back
+ for intf in self._interfaces:
+ self.session.set(self._base_path + [intf, 'disable'])
+ for option in self._options.get(intf, []):
+ self.session.set(self._base_path + [intf] + option.split())
+
+ self.session.commit()
+
+ # Validate interface description
+ for intf in self._interfaces:
+ self.assertEqual(Interface(intf).get_admin_state(), 'down')
def test_interface_description(self):
# Check if description can be added to interface and
@@ -150,6 +165,7 @@ class BasicInterfaceTest:
for intf in self._interfaces:
self.assertTrue(is_intf_addr_assigned(intf, addr))
+ self.assertEqual(Interface(intf).get_admin_state(), 'up')
def test_add_multiple_ip_addresses(self):
# Add address
@@ -174,7 +190,7 @@ class BasicInterfaceTest:
def test_ipv6_link_local_address(self):
# Common function for IPv6 link-local address assignemnts
if not self._test_ipv6:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -201,7 +217,7 @@ class BasicInterfaceTest:
def test_interface_mtu(self):
if not self._test_mtu:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
for intf in self._interfaces:
base = self._base_path + [intf]
@@ -221,7 +237,7 @@ class BasicInterfaceTest:
# Testcase if MTU can be changed to 1200 on non IPv6
# enabled interfaces
if not self._test_mtu:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
old_mtu = self._mtu
self._mtu = '1200'
@@ -244,9 +260,13 @@ class BasicInterfaceTest:
self._mtu = old_mtu
- def test_8021q_vlan_interfaces(self):
+ def test_vif_8021q_interfaces(self):
+ # XXX: This testcase is not allowed to run as first testcase, reason
+ # is the Wireless test will first load the wifi kernel hwsim module
+ # which creates a wlan0 and wlan1 interface which will fail the
+ # tearDown() test in the end that no interface is allowed to survive!
if not self._test_vlan:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -269,11 +289,16 @@ class BasicInterfaceTest:
tmp = read_file(f'/sys/class/net/{vif}/mtu')
self.assertEqual(tmp, self._mtu)
+ self.assertEqual(Interface(vif).get_admin_state(), 'up')
- def test_8021ad_qinq_vlan_interfaces(self):
+ def test_vif_s_8021ad_vlan_interfaces(self):
+ # XXX: This testcase is not allowed to run as first testcase, reason
+ # is the Wireless test will first load the wifi kernel hwsim module
+ # which creates a wlan0 and wlan1 interface which will fail the
+ # tearDown() test in the end that no interface is allowed to survive!
if not self._test_qinq:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -304,7 +329,7 @@ class BasicInterfaceTest:
def test_interface_ip_options(self):
if not self._test_ip:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
for interface in self._interfaces:
arp_tmo = '300'
@@ -355,7 +380,7 @@ class BasicInterfaceTest:
def test_interface_ipv6_options(self):
if not self._test_ipv6:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
for interface in self._interfaces:
dad_transmits = '10'
@@ -379,7 +404,7 @@ class BasicInterfaceTest:
def test_dhcpv6pd_auto_sla_id(self):
if not self._test_ipv6_pd:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
prefix_len = '56'
sla_len = str(64 - int(prefix_len))
@@ -435,7 +460,7 @@ class BasicInterfaceTest:
def test_dhcpv6pd_manual_sla_id(self):
if not self._test_ipv6_pd:
- self.skipTest('not enabled')
+ self.skipTest('not supported')
prefix_len = '56'
sla_len = str(64 - int(prefix_len))
diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py
index f42ec3e9b..560bfb92b 100755
--- a/smoketest/scripts/cli/test_interfaces_bonding.py
+++ b/smoketest/scripts/cli/test_interfaces_bonding.py
@@ -60,8 +60,8 @@ class BondingInterfaceTest(BasicInterfaceTest.BaseTest):
slaves = read_file(f'/sys/class/net/{interface}/bonding/slaves').split()
self.assertListEqual(slaves, self._members)
- def test_8021q_vlan_interfaces(self):
- super().test_8021q_vlan_interfaces()
+ def test_vif_8021q_interfaces(self):
+ super().test_vif_8021q_interfaces()
for interface in self._interfaces:
slaves = read_file(f'/sys/class/net/{interface}/bonding/slaves').split()
diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py
index 464226b6f..8f03290be 100755
--- a/smoketest/scripts/cli/test_interfaces_bridge.py
+++ b/smoketest/scripts/cli/test_interfaces_bridge.py
@@ -25,6 +25,7 @@ from netifaces import interfaces
from vyos.ifconfig import Section
from vyos.util import cmd
from vyos.util import read_file
+from vyos.validate import is_intf_addr_assigned
class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
def setUp(self):
@@ -32,7 +33,6 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
self._test_ipv6 = True
self._test_ipv6_pd = True
self._test_vlan = True
- self._test_qinq = True
self._base_path = ['interfaces', 'bridge']
self._mirror_interfaces = ['dum21354']
self._members = []
@@ -53,6 +53,12 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
super().setUp()
+ def tearDown(self):
+ for intf in self._interfaces:
+ self.session.delete(self._base_path + [intf])
+
+ super().tearDown()
+
def test_add_remove_bridge_member(self):
# Add member interfaces to bridge and set STP cost/priority
for interface in self._interfaces:
@@ -87,12 +93,22 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
self.session.commit()
+ def test_vif_8021q_interfaces(self):
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ self.session.set(base + ['enable-vlan'])
+ super().test_vif_8021q_interfaces()
+
def test_bridge_vlan_filter(self):
+
+ vif_vlan = 2
# Add member interface to bridge and set VLAN filter
for interface in self._interfaces:
base = self._base_path + [interface]
- self.session.set(base + ['vif', '1', 'address', '192.0.2.1/24'])
- self.session.set(base + ['vif', '2', 'address', '192.0.3.1/24'])
+ self.session.set(base + ['enable-vlan'])
+ self.session.set(base + ['address', '192.0.2.1/24'])
+ self.session.set(base + ['vif', str(vif_vlan), 'address', '192.0.3.1/24'])
+ self.session.set(base + ['vif', str(vif_vlan), 'mtu', self._mtu])
vlan_id = 101
allowed_vlan = 2
@@ -182,10 +198,13 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
for vif in vifs:
# member interface must be assigned to the bridge
self.assertTrue(os.path.exists(f'/sys/class/net/{interface}/lower_{member}.{vif}'))
-
- # remove VLAN interfaces
- for vif in vifs:
- self.session.delete(['interfaces', 'ethernet', member, 'vif', vif])
+
+ # delete all members
+ for interface in self._interfaces:
+ for member in self._members:
+ for vif in vifs:
+ self.session.delete(['interfaces', 'ethernet', member, 'vif', vif])
+ self.session.delete(['interfaces', 'bridge', interface, 'member', 'interface', f'{member}.{vif}'])
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py
index 42c1f15df..9d896f690 100755
--- a/smoketest/scripts/cli/test_interfaces_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_ethernet.py
@@ -78,8 +78,13 @@ class EthernetInterfaceTest(BasicInterfaceTest.BaseTest):
self.session.set(self._base_path + [interface, 'speed', 'auto'])
self.session.set(self._base_path + [interface, 'hw-id', self._macs[interface]])
- super().tearDown()
+ # Tear down mirror interfaces for SPAN (Switch Port Analyzer)
+ for span in self._mirror_interfaces:
+ section = Section.section(span)
+ self.session.delete(['interfaces', section, span])
+ self.session.commit()
+ del self.session
def test_dhcp_disable_interface(self):
# When interface is configured as admin down, it must be admin down
@@ -193,12 +198,12 @@ if __name__ == '__main__':
# Generate mandatory SSL certificate
tmp = f'openssl req -newkey rsa:4096 -new -nodes -x509 -days 3650 '\
f'-keyout {ssl_key} -out {ssl_cert} -subj {subject}'
- print(cmd(tmp))
+ cmd(tmp)
if not os.path.isfile(ca_cert):
# Generate "CA"
tmp = f'openssl req -new -x509 -key {ssl_key} -out {ca_cert} -subj {subject}'
- print(cmd(tmp))
+ cmd(tmp)
for file in [ca_cert, ssl_cert, ssl_key]:
cmd(f'sudo chown radius_priv_user:vyattacfg {file}')
diff --git a/smoketest/scripts/cli/test_interfaces_loopback.py b/smoketest/scripts/cli/test_interfaces_loopback.py
index 79225a1bd..36000c3ff 100755
--- a/smoketest/scripts/cli/test_interfaces_loopback.py
+++ b/smoketest/scripts/cli/test_interfaces_loopback.py
@@ -17,6 +17,8 @@
import unittest
from base_interfaces_test import BasicInterfaceTest
+from netifaces import interfaces
+
from vyos.validate import is_intf_addr_assigned
class LoopbackInterfaceTest(BasicInterfaceTest.BaseTest):
@@ -27,6 +29,15 @@ class LoopbackInterfaceTest(BasicInterfaceTest.BaseTest):
self._base_path = ['interfaces', 'loopback']
self._interfaces = ['lo']
+ def tearDown(self):
+ self.session.delete(self._base_path)
+ self.session.commit()
+ del self.session
+
+ # loopback interface must persist!
+ for intf in self._interfaces:
+ self.assertIn(intf, interfaces())
+
def test_add_single_ip_address(self):
super().test_add_single_ip_address()
for addr in self._loopback_addresses:
@@ -37,5 +48,8 @@ class LoopbackInterfaceTest(BasicInterfaceTest.BaseTest):
for addr in self._loopback_addresses:
self.assertTrue(is_intf_addr_assigned('lo', addr))
+ def test_interface_disable(self):
+ self.skipTest('not supported')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index f67b813af..8405fc7d0 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -84,7 +84,6 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
self.session.delete(['interfaces', 'dummy', source_if])
super().tearDown()
-
def test_ipv4_encapsulations(self):
# When running tests ensure that for certain encapsulation types the
# local and remote IP address is actually an IPv4 address
@@ -106,7 +105,6 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
-
self.session.set(self._base_path + [interface, 'source-interface', source_if])
# Source interface can not be used with sit and gre-bridge
@@ -130,6 +128,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
+ self.assertTrue(conf['linkinfo']['info_data']['pmtudisc'])
# cleanup this instance
self.session.delete(self._base_path + [interface])
@@ -177,7 +176,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
self.assertEqual(encapsulation, conf['link_type'])
self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
- self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
+ self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
# cleanup this instance
self.session.delete(self._base_path + [interface])
@@ -203,5 +202,31 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
# Check if commit is ok
self.session.commit()
+ def test_tunnel_parameters_gre(self):
+ interface = f'tun1030'
+ gre_key = '10'
+ encapsulation = 'gre'
+ tos = '20'
+
+ self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
+ self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
+ self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
+
+ self.session.set(self._base_path + [interface, 'parameters', 'ip', 'no-pmtu-discovery'])
+ self.session.set(self._base_path + [interface, 'parameters', 'ip', 'key', gre_key])
+ self.session.set(self._base_path + [interface, 'parameters', 'ip', 'tos', tos])
+
+ # Check if commit is ok
+ self.session.commit()
+
+ conf = tunnel_conf(interface)
+ self.assertEqual(mtu, conf['mtu'])
+ self.assertEqual(interface, conf['ifname'])
+ self.assertEqual(encapsulation, conf['link_type'])
+ self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
+ self.assertEqual(0, conf['linkinfo']['info_data']['ttl'])
+ self.assertFalse( conf['linkinfo']['info_data']['pmtudisc'])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index eede042de..68081e56f 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -27,7 +27,11 @@ from vyos.util import read_file
PROCESS_NAME = 'sshd'
SSHD_CONF = '/run/sshd/sshd_config'
base_path = ['service', 'ssh']
-vrf = 'ssh-test'
+vrf = 'mgmt'
+
+key_rsa = '/etc/ssh/ssh_host_rsa_key'
+key_dsa = '/etc/ssh/ssh_host_dsa_key'
+key_ed25519 = '/etc/ssh/ssh_host_ed25519_key'
def get_config_value(key):
tmp = read_file(SSHD_CONF)
@@ -47,6 +51,10 @@ class TestServiceSSH(unittest.TestCase):
self.session.commit()
del self.session
+ self.assertTrue(os.path.isfile(key_rsa))
+ self.assertTrue(os.path.isfile(key_dsa))
+ self.assertTrue(os.path.isfile(key_ed25519))
+
def test_ssh_default(self):
# Check if SSH service runs with default settings - used for checking
# behavior of <defaultValue> in XML definition
diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py
index 8fc18ba88..4ad8d537d 100755
--- a/smoketest/scripts/cli/test_system_ip.py
+++ b/smoketest/scripts/cli/test_system_ip.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 Francois Mertz fireboxled@gmail.com
+# Copyright (C) 2020 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py
new file mode 100755
index 000000000..df69739eb
--- /dev/null
+++ b/smoketest/scripts/cli/test_system_ipv6.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 vyos.configsession import ConfigSession
+from vyos.util import read_file
+
+base_path = ['system', 'ipv6']
+
+file_forwarding = '/proc/sys/net/ipv6/conf/all/forwarding'
+file_disable = '/etc/modprobe.d/vyos_disable_ipv6.conf'
+file_dad = '/proc/sys/net/ipv6/conf/all/accept_dad'
+file_multipath = '/proc/sys/net/ipv6/fib_multipath_hash_policy'
+
+class TestSystemIPv6(unittest.TestCase):
+ def setUp(self):
+ self.session = ConfigSession(os.getpid())
+
+ def tearDown(self):
+ self.session.delete(base_path)
+ self.session.commit()
+ del self.session
+
+ def test_system_ipv6_forwarding(self):
+ # Test if IPv6 forwarding can be disabled globally, default is '1'
+ # which means forwearding enabled
+ self.assertEqual(read_file(file_forwarding), '1')
+
+ self.session.set(base_path + ['disable-forwarding'])
+ self.session.commit()
+
+ self.assertEqual(read_file(file_forwarding), '0')
+
+ def test_system_ipv6_disable(self):
+ # Do not assign any IPv6 address on interfaces, this requires a reboot
+ # which can not be tested, but we can read the config file :)
+ self.session.set(base_path + ['disable'])
+ self.session.commit()
+
+ # Verify configuration file
+ self.assertEqual(read_file(file_disable), 'options ipv6 disable_ipv6=1')
+
+ def test_system_ipv6_strict_dad(self):
+ # This defaults to 1
+ self.assertEqual(read_file(file_dad), '1')
+
+ # Do not assign any IPv6 address on interfaces, this requires a reboot
+ # which can not be tested, but we can read the config file :)
+ self.session.set(base_path + ['strict-dad'])
+ self.session.commit()
+
+ # Verify configuration file
+ self.assertEqual(read_file(file_dad), '2')
+
+ def test_system_ipv6_multipath(self):
+ # This defaults to 0
+ self.assertEqual(read_file(file_multipath), '0')
+
+ # Do not assign any IPv6 address on interfaces, this requires a reboot
+ # which can not be tested, but we can read the config file :)
+ self.session.set(base_path + ['multipath', 'layer4-hashing'])
+ self.session.commit()
+
+ # Verify configuration file
+ self.assertEqual(read_file(file_multipath), '1')
+
+ def test_system_ipv6_neighbor_table_size(self):
+ # Maximum number of entries to keep in the ARP cache, the
+ # default is 8192
+
+ gc_thresh3 = '/proc/sys/net/ipv6/neigh/default/gc_thresh3'
+ gc_thresh2 = '/proc/sys/net/ipv6/neigh/default/gc_thresh2'
+ gc_thresh1 = '/proc/sys/net/ipv6/neigh/default/gc_thresh1'
+ self.assertEqual(read_file(gc_thresh3), '8192')
+ self.assertEqual(read_file(gc_thresh2), '4096')
+ self.assertEqual(read_file(gc_thresh1), '1024')
+
+ for size in [1024, 2048, 4096, 8192, 16384, 32768]:
+ self.session.set(base_path + ['neighbor', 'table-size', str(size)])
+ self.session.commit()
+
+ self.assertEqual(read_file(gc_thresh3), str(size))
+ self.assertEqual(read_file(gc_thresh2), str(size // 2))
+ self.assertEqual(read_file(gc_thresh1), str(size // 8))
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py
index 986c8dfb2..edb6ad94d 100755
--- a/smoketest/scripts/cli/test_system_ntp.py
+++ b/smoketest/scripts/cli/test_system_ntp.py
@@ -26,7 +26,7 @@ from vyos.util import read_file
from vyos.util import process_named_running
PROCESS_NAME = 'ntpd'
-NTP_CONF = '/etc/ntp.conf'
+NTP_CONF = '/run/ntpd/ntpd.conf'
base_path = ['system', 'ntp']
def get_config_value(key):
@@ -47,6 +47,8 @@ class TestSystemNTP(unittest.TestCase):
self.session.commit()
del self.session
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
def test_ntp_options(self):
# Test basic NTP support with multiple servers and their options
servers = ['192.0.2.1', '192.0.2.2']
@@ -76,7 +78,7 @@ class TestSystemNTP(unittest.TestCase):
self.assertTrue(process_named_running(PROCESS_NAME))
def test_ntp_clients(self):
- """ Test the allowed-networks statement """
+ # Test the allowed-networks statement
listen_address = ['127.0.0.1', '::1']
for listen in listen_address:
self.session.set(base_path + ['listen-address', listen])
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index 7bcfea861..5270e758a 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -14,53 +14,111 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re
import os
import unittest
+from netifaces import interfaces
-from vyos.configsession import ConfigSession, ConfigSessionError
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
from vyos.util import read_file
from vyos.validate import is_intf_addr_assigned
+from vyos.ifconfig import Interface
+
+base_path = ['vrf']
+vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo']
class VRFTest(unittest.TestCase):
def setUp(self):
self.session = ConfigSession(os.getpid())
- self._vrfs = ['red', 'green', 'blue']
def tearDown(self):
# delete all VRFs
- self.session.delete(['vrf'])
+ self.session.delete(base_path)
self.session.commit()
- del self.session
+ for vrf in vrfs:
+ self.assertNotIn(vrf, interfaces())
def test_vrf_table_id(self):
- table = 1000
- for vrf in self._vrfs:
- base = ['vrf', 'name', vrf]
- description = "VyOS-VRF-" + vrf
+ table = '1000'
+ for vrf in vrfs:
+ base = base_path + ['name', vrf]
+ description = f'VyOS-VRF-{vrf}'
self.session.set(base + ['description', description])
# check validate() - a table ID is mandatory
with self.assertRaises(ConfigSessionError):
self.session.commit()
- self.session.set(base + ['table', str(table)])
- table += 1
+ self.session.set(base + ['table', table])
+ if vrf == 'green':
+ self.session.set(base + ['disable'])
+
+ table = str(int(table) + 1)
# commit changes
self.session.commit()
+ # Verify VRF configuration
+ table = '1000'
+ iproute2_config = read_file('/etc/iproute2/rt_tables.d/vyos-vrf.conf')
+ for vrf in vrfs:
+ description = f'VyOS-VRF-{vrf}'
+ self.assertTrue(vrf in interfaces())
+ vrf_if = Interface(vrf)
+ # validate proper interface description
+ self.assertEqual(vrf_if.get_alias(), description)
+ # validate admin up/down state of VRF
+ state = 'up'
+ if vrf == 'green':
+ state = 'down'
+ self.assertEqual(vrf_if.get_admin_state(), state)
+
+ # Test the iproute2 lookup file, syntax is as follows:
+ #
+ # # id vrf name comment
+ # 1000 red # VyOS-VRF-red
+ # 1001 green # VyOS-VRF-green
+ # ...
+ regex = f'{table}\s+{vrf}\s+#\s+{description}'
+ self.assertTrue(re.findall(regex, iproute2_config))
+ table = str(int(table) + 1)
+
def test_vrf_loopback_ips(self):
- table = 1000
- for vrf in self._vrfs:
- base = ['vrf', 'name', vrf]
+ table = '2000'
+ for vrf in vrfs:
+ base = base_path + ['name', vrf]
self.session.set(base + ['table', str(table)])
- table += 1
+ table = str(int(table) + 1)
# commit changes
self.session.commit()
- for vrf in self._vrfs:
+
+ # Verify VRF configuration
+ for vrf in vrfs:
+ self.assertTrue(vrf in interfaces())
self.assertTrue(is_intf_addr_assigned(vrf, '127.0.0.1'))
self.assertTrue(is_intf_addr_assigned(vrf, '::1'))
+ def test_vrf_table_id_is_unalterable(self):
+ # Linux Kernel prohibits the change of a VRF table on the fly.
+ # VRF must be deleted and recreated!
+ table = '1000'
+ vrf = vrfs[0]
+ base = base_path + ['name', vrf]
+ self.session.set(base + ['table', table])
+
+ # commit changes
+ self.session.commit()
+
+ # Check if VRF has been created
+ self.assertTrue(vrf in interfaces())
+
+ table = str(int(table) + 1)
+ self.session.set(base + ['table', table])
+ # check validate() - table ID can not be altered!
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+
if __name__ == '__main__':
- unittest.main(verbosity=2)
+ unittest.main(verbosity=2, failfast=True)
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 7af3e3d7c..fd4ffed9a 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -41,26 +41,6 @@ from vyos import ConfigError
from vyos import airbag
airbag.enable()
-def helper_check_removed_vlan(conf,bridge,key,key_mangling):
- key_update = re.sub(key_mangling[0], key_mangling[1], key)
- if dict_search('member.interface', bridge):
- for interface in bridge['member']['interface']:
- tmp = leaf_node_changed(conf, ['member', 'interface',interface,key])
- if tmp:
- if 'member' in bridge:
- if 'interface' in bridge['member']:
- if interface in bridge['member']['interface']:
- bridge['member']['interface'][interface].update({f'{key_update}_removed': tmp })
- else:
- bridge['member']['interface'].update({interface: {f'{key_update}_removed': tmp }})
- else:
- bridge['member'].update({ 'interface': {interface: {f'{key_update}_removed': tmp }}})
- else:
- bridge.update({'member': { 'interface': {interface: {f'{key_update}_removed': tmp }}}})
-
- return bridge
-
-
def get_config(config=None):
"""
Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
@@ -74,18 +54,12 @@ def get_config(config=None):
bridge = get_interface_dict(conf, base)
# determine which members have been removed
- tmp = node_changed(conf, ['member', 'interface'])
+ tmp = node_changed(conf, ['member', 'interface'], key_mangling=('-', '_'))
if tmp:
if 'member' in bridge:
bridge['member'].update({'interface_remove': tmp })
else:
bridge.update({'member': {'interface_remove': tmp }})
-
-
- # determine which members vlan have been removed
-
- bridge = helper_check_removed_vlan(conf,bridge,'native-vlan',('-', '_'))
- bridge = helper_check_removed_vlan(conf,bridge,'allowed-vlan',('-', '_'))
if dict_search('member.interface', bridge):
# XXX: T2665: we need a copy of the dict keys for iteration, else we will get:
@@ -99,7 +73,6 @@ def get_config(config=None):
# the default dictionary is not properly paged into the dict (see T2665)
# thus we will ammend it ourself
default_member_values = defaults(base + ['member', 'interface'])
- vlan_aware = False
for interface,interface_config in bridge['member']['interface'].items():
bridge['member']['interface'][interface] = dict_merge(
default_member_values, bridge['member']['interface'][interface])
@@ -120,19 +93,11 @@ def get_config(config=None):
# Bridge members must not have an assigned address
tmp = has_address_configured(conf, interface)
if tmp: bridge['member']['interface'][interface].update({'has_address' : ''})
-
+
# VLAN-aware bridge members must not have VLAN interface configuration
- if 'native_vlan' in interface_config:
- vlan_aware = True
-
- if 'allowed_vlan' in interface_config:
- vlan_aware = True
-
-
- if vlan_aware:
- tmp = has_vlan_subinterface_configured(conf,interface)
- if tmp:
- if tmp: bridge['member']['interface'][interface].update({'has_vlan' : ''})
+ tmp = has_vlan_subinterface_configured(conf,interface)
+ if 'enable_vlan' in bridge and tmp:
+ bridge['member']['interface'][interface].update({'has_vlan' : ''})
return bridge
@@ -142,8 +107,8 @@ def verify(bridge):
verify_dhcpv6(bridge)
verify_vrf(bridge)
-
- vlan_aware = False
+
+ ifname = bridge['ifname']
if dict_search('member.interface', bridge):
for interface, interface_config in bridge['member']['interface'].items():
@@ -166,31 +131,24 @@ def verify(bridge):
if 'has_address' in interface_config:
raise ConfigError(error_msg + 'it has an address assigned!')
-
- if 'has_vlan' in interface_config:
- raise ConfigError(error_msg + 'it has an VLAN subinterface assigned!')
-
- # VLAN-aware bridge members must not have VLAN interface configuration
- if 'native_vlan' in interface_config:
- vlan_aware = True
-
- if 'allowed_vlan' in interface_config:
- vlan_aware = True
-
- if vlan_aware and 'wlan' in interface:
- raise ConfigError(error_msg + 'VLAN aware cannot be set!')
-
- if 'allowed_vlan' in interface_config:
- for vlan in interface_config['allowed_vlan']:
- if re.search('[0-9]{1,4}-[0-9]{1,4}', vlan):
- vlan_range = vlan.split('-')
- if int(vlan_range[0]) <1 and int(vlan_range[0])>4094:
- raise ConfigError('VLAN ID must be between 1 and 4094')
- if int(vlan_range[1]) <1 and int(vlan_range[1])>4094:
- raise ConfigError('VLAN ID must be between 1 and 4094')
- else:
- if int(vlan) <1 and int(vlan)>4094:
- raise ConfigError('VLAN ID must be between 1 and 4094')
+
+ if 'enable_vlan' in bridge:
+ if 'has_vlan' in interface_config:
+ raise ConfigError(error_msg + 'it has an VLAN subinterface assigned!')
+
+ if 'wlan' in interface:
+ raise ConfigError(error_msg + 'VLAN aware cannot be set!')
+ else:
+ for option in ['allowed_vlan', 'native_vlan']:
+ if option in interface_config:
+ raise ConfigError('Can not use VLAN options on non VLAN aware bridge')
+
+ if 'enable_vlan' in bridge:
+ if dict_search('vif.1', bridge):
+ raise ConfigError(f'VLAN 1 sub interface cannot be set for VLAN aware bridge {ifname}, and VLAN 1 is always the parent interface')
+ else:
+ if dict_search('vif', bridge):
+ raise ConfigError(f'You must first activate "enable-vlan" of {ifname} bridge to use "vif"')
return None
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index e4a6a5ec1..ee6f05fcd 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -17,6 +17,7 @@
import os
import re
+from glob import glob
from sys import exit
from ipaddress import IPv4Address
from ipaddress import IPv4Network
@@ -488,14 +489,9 @@ def apply(openvpn):
# Do some cleanup when OpenVPN is disabled/deleted
if 'deleted' in openvpn or 'disable' in openvpn:
- # cleanup old configuration files
- cleanup = []
- cleanup.append(cfg_file.format(**openvpn))
- cleanup.append(openvpn['auth_user_pass_file'])
-
- for file in cleanup:
- if os.path.isfile(file):
- os.unlink(file)
+ for cleanup_file in glob(f'/run/openvpn/{interface}.*'):
+ if os.path.isfile(cleanup_file):
+ os.unlink(cleanup_file)
if interface in interfaces():
VTunIf(interface).remove()
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index 7cfc76aa0..3e6320f02 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -58,7 +58,7 @@ def get_config(config=None):
# Determine which Wireguard peer has been removed.
# Peers can only be removed with their public key!
dict = {}
- tmp = node_changed(conf, ['peer'])
+ tmp = node_changed(conf, ['peer'], key_mangling=('-', '_'))
for peer in (tmp or []):
pubkey = leaf_node_changed(conf, ['peer', peer, 'pubkey'])
if pubkey:
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index 2d98cb11b..dae958774 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2021 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
@@ -26,6 +26,7 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.template import render
+from vyos.template import is_ip_network
from vyos.util import cmd
from vyos.util import check_kmod
from vyos.util import dict_search
@@ -68,9 +69,9 @@ def verify_rule(config, err_msg):
'ports can only be specified when protocol is '\
'either tcp, udp or tcp_udp!')
- if '/' in (dict_search('translation.address', config) or []):
+ if is_ip_network(dict_search('translation.address', config)):
raise ConfigError(f'{err_msg}\n' \
- 'Cannot use ports with an IPv4net type translation address as it\n' \
+ 'Cannot use ports with an IPv4 network as translation address as it\n' \
'statically maps a whole network of addresses onto another\n' \
'network of addresses')
@@ -147,7 +148,7 @@ def verify(nat):
addr = dict_search('translation.address', config)
if addr != None:
- if addr != 'masquerade':
+ if addr != 'masquerade' and not is_ip_network(addr):
for ip in addr.split('-'):
if not is_addr_assigned(ip):
print(f'WARNING: IP address {ip} does not exist on the system!')
diff --git a/src/conf_mode/ntp.py b/src/conf_mode/ntp.py
index b102b3e9e..52070aabc 100755
--- a/src/conf_mode/ntp.py
+++ b/src/conf_mode/ntp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2021 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
@@ -24,7 +24,7 @@ from vyos.template import render
from vyos import airbag
airbag.enable()
-config_file = r'/etc/ntp.conf'
+config_file = r'/run/ntpd/ntpd.conf'
systemd_override = r'/etc/systemd/system/ntp.service.d/override.conf'
def get_config(config=None):
@@ -33,8 +33,11 @@ def get_config(config=None):
else:
conf = Config()
base = ['system', 'ntp']
+ if not conf.exists(base):
+ return None
ntp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ ntp['config_file'] = config_file
return ntp
def verify(ntp):
@@ -42,7 +45,7 @@ def verify(ntp):
if not ntp:
return None
- if len(ntp.get('allow_clients', {})) and not (len(ntp.get('server', {})) > 0):
+ if 'allow_clients' in ntp and 'server' not in ntp:
raise ConfigError('NTP server not configured')
verify_vrf(ntp)
@@ -53,7 +56,7 @@ def generate(ntp):
if not ntp:
return None
- render(config_file, 'ntp/ntp.conf.tmpl', ntp)
+ render(config_file, 'ntp/ntpd.conf.tmpl', ntp)
render(systemd_override, 'ntp/override.conf.tmpl', ntp)
return None
diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py
index c4024dce4..013f22665 100755
--- a/src/conf_mode/policy-local-route.py
+++ b/src/conf_mode/policy-local-route.py
@@ -40,7 +40,7 @@ def get_config(config=None):
# delete policy local-route
dict = {}
- tmp = node_changed(conf, ['policy', 'local-route', 'rule'])
+ tmp = node_changed(conf, ['policy', 'local-route', 'rule'], key_mangling=('-', '_'))
if tmp:
for rule in (tmp or []):
src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source'])
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index f8e34285e..39d367b97 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -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/>.
+import os
+
from sys import exit
from vyos.config import Config
@@ -29,6 +31,14 @@ airbag.enable()
config_file = r'/tmp/bgp.frr'
+DEBUG = os.path.exists('/tmp/bgp.debug')
+if DEBUG:
+ import logging
+ lg = logging.getLogger("vyos.frr")
+ lg.setLevel(logging.DEBUG)
+ ch = logging.StreamHandler()
+ lg.addHandler(ch)
+
def get_config():
conf = Config()
base = ['protocols', 'bgp']
@@ -78,8 +88,13 @@ def verify(bgp):
if neighbor == 'neighbor':
# remote-as must be either set explicitly for the neighbor
# or for the entire peer-group
- if 'remote_as' not in peer_config:
- if 'peer_group' not in peer_config or 'remote_as' not in asn_config['peer_group'][peer_config['peer_group']]:
+ if 'interface' in peer_config:
+ if 'remote_as' not in peer_config['interface']:
+ if 'peer_group' not in peer_config['interface'] or 'remote_as' not in asn_config['peer_group'][ peer_config['interface']['peer_group'] ]:
+ raise ConfigError('Remote AS must be set for neighbor or peer-group!')
+
+ elif 'remote_as' not in peer_config:
+ if 'peer_group' not in peer_config or 'remote_as' not in asn_config['peer_group'][ peer_config['peer_group'] ]:
raise ConfigError('Remote AS must be set for neighbor or peer-group!')
for afi in ['ipv4_unicast', 'ipv6_unicast']:
@@ -144,6 +159,21 @@ def apply(bgp):
frr_cfg.load_configuration(daemon='bgpd')
frr_cfg.modify_section(f'router bgp \S+', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['new_frr_config'])
+
+ # Debugging
+ if DEBUG:
+ from pprint import pprint
+ print('')
+ print('--------- DEBUGGING ----------')
+ pprint(dir(frr_cfg))
+ print('Existing config:\n')
+ for line in frr_cfg.original_config:
+ print(line)
+ print(f'Replacement config:\n')
+ print(f'{bgp["new_frr_config"]}')
+ print(f'Modified config:\n')
+ print(f'{frr_cfg}')
+
frr_cfg.commit_configuration(daemon='bgpd')
# If FRR config is blank, rerun the blank commit x times due to frr-reload
@@ -152,14 +182,6 @@ def apply(bgp):
for a in range(5):
frr_cfg.commit_configuration(daemon='bgpd')
- # Debugging
- '''
- print('')
- print('--------- DEBUGGING ----------')
- print(f'Existing config:\n{frr_cfg["original_config"]}\n\n')
- print(f'Replacement config:\n{bgp["new_frr_config"]}\n\n')
- print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n')
- '''
return None
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
new file mode 100644
index 000000000..73c244571
--- /dev/null
+++ b/src/conf_mode/protocols_ospf.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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.configdict import dict_merge
+from vyos.template import render
+from vyos.template import render_to_string
+from vyos.util import call
+from vyos.util import dict_search
+from vyos import ConfigError
+from vyos import frr
+from vyos import airbag
+airbag.enable()
+
+config_file = r'/tmp/ospf.frr'
+
+DEBUG = os.path.exists('/tmp/ospf.debug')
+if DEBUG:
+ import logging
+ lg = logging.getLogger("vyos.frr")
+ lg.setLevel(logging.DEBUG)
+ ch = logging.StreamHandler()
+ lg.addHandler(ch)
+
+def get_config():
+ conf = Config()
+ base = ['protocols', 'ospf']
+ ospf = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ return ospf
+
+def verify(ospf):
+ if not ospf:
+ return None
+
+ return None
+
+def generate(ospf):
+ if not ospf:
+ ospf['new_frr_config'] = ''
+ return None
+
+ # render(config) not needed, its only for debug
+ render(config_file, 'frr/ospf.frr.tmpl', ospf)
+ ospf['new_frr_config'] = render_to_string('frr/ospf.frr.tmpl', ospf)
+
+ return None
+
+def apply(ospf):
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+ frr_cfg.load_configuration(daemon='ospfd')
+ frr_cfg.modify_section(f'router ospf', '')
+
+ # Debugging
+ if DEBUG:
+ from pprint import pprint
+ print('')
+ print('--------- DEBUGGING ----------')
+ pprint(dir(frr_cfg))
+ print('Existing config:\n')
+ for line in frr_cfg.original_config:
+ print(line)
+ print(f'Replacement config:\n')
+ print(f'{ospf["new_frr_config"]}')
+ print(f'Modified config:\n')
+ print(f'{frr_cfg}')
+
+ frr_cfg.commit_configuration(daemon='ospfd')
+
+ # If FRR config is blank, rerun the blank commit x times due to frr-reload
+ # behavior/bug not properly clearing out on one commit.
+ if ospf['new_frr_config'] == '':
+ for a in range(5):
+ frr_cfg.commit_configuration(daemon='ospfd')
+
+ 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/ssh.py b/src/conf_mode/ssh.py
index 8eeb0a7c1..67724b043 100755
--- a/src/conf_mode/ssh.py
+++ b/src/conf_mode/ssh.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2021 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
@@ -17,6 +17,8 @@
import os
from sys import exit
+from syslog import syslog
+from syslog import LOG_INFO
from vyos.config import Config
from vyos.configdict import dict_merge
@@ -31,6 +33,10 @@ airbag.enable()
config_file = r'/run/sshd/sshd_config'
systemd_override = r'/etc/systemd/system/ssh.service.d/override.conf'
+key_rsa = '/etc/ssh/ssh_host_rsa_key'
+key_dsa = '/etc/ssh/ssh_host_dsa_key'
+key_ed25519 = '/etc/ssh/ssh_host_ed25519_key'
+
def get_config(config=None):
if config:
conf = config
@@ -66,6 +72,18 @@ def generate(ssh):
return None
+ # This usually happens only once on a fresh system, SSH keys need to be
+ # freshly generted, one per every system!
+ if not os.path.isfile(key_rsa):
+ syslog(LOG_INFO, 'SSH RSA host key not found, generating new key!')
+ call(f'ssh-keygen -q -N "" -t rsa -f {key_rsa}')
+ if not os.path.isfile(key_dsa):
+ syslog(LOG_INFO, 'SSH DSA host key not found, generating new key!')
+ call(f'ssh-keygen -q -N "" -t dsa -f {key_dsa}')
+ if not os.path.isfile(key_ed25519):
+ syslog(LOG_INFO, 'SSH ed25519 host key not found, generating new key!')
+ call(f'ssh-keygen -q -N "" -t ed25519 -f {key_ed25519}')
+
render(config_file, 'ssh/sshd_config.tmpl', ssh)
render(systemd_override, 'ssh/override.conf.tmpl', ssh)
# Reload systemd manager configuration
diff --git a/src/conf_mode/system-option.py b/src/conf_mode/system-option.py
index 910c14474..454611c55 100755
--- a/src/conf_mode/system-option.py
+++ b/src/conf_mode/system-option.py
@@ -87,10 +87,10 @@ def apply(options):
# Ctrl-Alt-Delete action
if os.path.exists(systemd_action_file):
os.unlink(systemd_action_file)
- if 'ctrl_alt_del' in options:
- if options['ctrl_alt_del'] == 'reboot':
+ if 'ctrl_alt_delete' in options:
+ if options['ctrl_alt_delete'] == 'reboot':
os.symlink('/lib/systemd/system/reboot.target', systemd_action_file)
- elif options['ctrl_alt_del'] == 'poweroff':
+ elif options['ctrl_alt_delete'] == 'poweroff':
os.symlink('/lib/systemd/system/poweroff.target', systemd_action_file)
# Configure HTTP client
@@ -104,11 +104,11 @@ def apply(options):
os.unlink(ssh_config)
# Reboot system on kernel panic
+ timeout = '0'
+ if 'reboot_on_panic' in options:
+ timeout = '60'
with open('/proc/sys/kernel/panic', 'w') as f:
- if 'reboot_on_panic' in options:
- f.write('60')
- else:
- f.write('0')
+ f.write(timeout)
# tuned - performance tuning
if 'performance' in options:
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index c4ba859b7..6c6e219a5 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2021 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
@@ -17,29 +17,20 @@
import os
from sys import exit
-from copy import deepcopy
from json import loads
from vyos.config import Config
-from vyos.configdict import list_diff
+from vyos.configdict import node_changed
from vyos.ifconfig import Interface
-from vyos.util import read_file, cmd
-from vyos import ConfigError
from vyos.template import render
-
+from vyos.util import cmd
+from vyos.util import dict_search
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
config_file = r'/etc/iproute2/rt_tables.d/vyos-vrf.conf'
-default_config_data = {
- 'bind_to_all': '0',
- 'deleted': False,
- 'vrf_add': [],
- 'vrf_existing': [],
- 'vrf_remove': []
-}
-
def _cmd(command):
cmd(command, raising=ConfigError, message='Error changing VRF')
@@ -81,112 +72,62 @@ def get_config(config=None):
conf = config
else:
conf = Config()
- vrf_config = deepcopy(default_config_data)
- cfg_base = ['vrf']
- if not conf.exists(cfg_base):
- # get all currently effetive VRFs and mark them for deletion
- vrf_config['vrf_remove'] = conf.list_effective_nodes(cfg_base + ['name'])
- else:
- # set configuration level base
- conf.set_level(cfg_base)
-
- # Should services be allowed to bind to all VRFs?
- if conf.exists(['bind-to-all']):
- vrf_config['bind_to_all'] = '1'
-
- # Determine vrf interfaces (currently effective) - to determine which
- # vrf interface is no longer present and needs to be removed
- eff_vrf = conf.list_effective_nodes(['name'])
- act_vrf = conf.list_nodes(['name'])
- vrf_config['vrf_remove'] = list_diff(eff_vrf, act_vrf)
-
- # read in individual VRF definition and build up
- # configuration
- for name in conf.list_nodes(['name']):
- vrf_inst = {
- 'description' : '',
- 'members': [],
- 'name' : name,
- 'table' : '',
- 'table_mod': False
- }
- conf.set_level(cfg_base + ['name', name])
-
- if conf.exists(['table']):
- # VRF table can't be changed on demand, thus we need to read in the
- # current and the effective routing table number
- act_table = conf.return_value(['table'])
- eff_table = conf.return_effective_value(['table'])
- vrf_inst['table'] = act_table
- if eff_table and eff_table != act_table:
- vrf_inst['table_mod'] = True
-
- if conf.exists(['description']):
- vrf_inst['description'] = conf.return_value(['description'])
-
- # append individual VRF configuration to global configuration list
- vrf_config['vrf_add'].append(vrf_inst)
-
- # set configuration level base
- conf.set_level(cfg_base)
-
- # check VRFs which need to be removed as they are not allowed to have
- # interfaces attached
- tmp = []
- for name in vrf_config['vrf_remove']:
- vrf_inst = {
- 'interfaces': [],
- 'name': name,
- 'routes': []
- }
-
- # find member interfaces of this particulat VRF
- vrf_inst['interfaces'] = vrf_interfaces(conf, name)
-
- # find routing protocols used by this VRF
- vrf_inst['routes'] = vrf_routing(conf, name)
-
- # append individual VRF configuration to temporary configuration list
- tmp.append(vrf_inst)
-
- # replace values in vrf_remove with list of dictionaries
- # as we need it in verify() - we can't delete a VRF with members attached
- vrf_config['vrf_remove'] = tmp
- return vrf_config
-
-def verify(vrf_config):
- # ensure VRF is not assigned to any interface
- for vrf in vrf_config['vrf_remove']:
- if len(vrf['interfaces']) > 0:
- raise ConfigError(f"VRF {vrf['name']} can not be deleted. It has active member interfaces!")
+ base = ['vrf']
+ vrf = conf.get_config_dict(base, get_first_key=True)
- if len(vrf['routes']) > 0:
- raise ConfigError(f"VRF {vrf['name']} can not be deleted. It has active routing protocols!")
+ # determine which VRF has been removed
+ for name in node_changed(conf, base + ['name']):
+ if 'vrf_remove' not in vrf:
+ vrf.update({'vrf_remove' : {}})
- table_ids = []
- for vrf in vrf_config['vrf_add']:
- # table id is mandatory
- if not vrf['table']:
- raise ConfigError(f"VRF {vrf['name']} table id is mandatory!")
+ vrf['vrf_remove'][name] = {}
+ # get VRF bound interfaces
+ interfaces = vrf_interfaces(conf, name)
+ if interfaces: vrf['vrf_remove'][name]['interface'] = interfaces
+ # get VRF bound routing instances
+ routes = vrf_routing(conf, name)
+ if routes: vrf['vrf_remove'][name]['route'] = routes
- # routing table id can't be changed - OS restriction
- if vrf['table_mod']:
- raise ConfigError(f"VRF {vrf['name']} table id modification is not possible!")
+ return vrf
- # VRf routing table ID must be unique on the system
- if vrf['table'] in table_ids:
- raise ConfigError(f"VRF {vrf['name']} table id {vrf['table']} is not unique!")
-
- table_ids.append(vrf['table'])
+def verify(vrf):
+ # ensure VRF is not assigned to any interface
+ if 'vrf_remove' in vrf:
+ for name, config in vrf['vrf_remove'].items():
+ if 'interface' in config:
+ raise ConfigError(f'Can not remove VRF "{name}", it still has '\
+ f'member interfaces!')
+ if 'route' in config:
+ raise ConfigError(f'Can not remove VRF "{name}", it still has '\
+ f'static routes installed!')
+
+ if 'name' in vrf:
+ table_ids = []
+ for name, config in vrf['name'].items():
+ # table id is mandatory
+ if 'table' not in config:
+ raise ConfigError(f'VRF "{name}" table id is mandatory!')
+
+ # routing table id can't be changed - OS restriction
+ if os.path.isdir(f'/sys/class/net/{name}'):
+ tmp = loads(cmd(f'ip -j -d link show {name}'))[0]
+ tmp = str(dict_search('linkinfo.info_data.table', tmp))
+ if tmp and tmp != config['table']:
+ raise ConfigError(f'VRF "{name}" table id modification not possible!')
+
+ # VRf routing table ID must be unique on the system
+ if config['table'] in table_ids:
+ raise ConfigError(f'VRF "{name}" table id is not unique!')
+ table_ids.append(config['table'])
return None
-def generate(vrf_config):
- render(config_file, 'vrf/vrf.conf.tmpl', vrf_config)
+def generate(vrf):
+ render(config_file, 'vrf/vrf.conf.tmpl', vrf)
return None
-def apply(vrf_config):
+def apply(vrf):
# Documentation
#
# - https://github.com/torvalds/linux/blob/master/Documentation/networking/vrf.txt
@@ -196,40 +137,48 @@ def apply(vrf_config):
# - https://netdevconf.info/1.2/slides/oct6/02_ahern_what_is_l3mdev_slides.pdf
# set the default VRF global behaviour
- bind_all = vrf_config['bind_to_all']
- if read_file('/proc/sys/net/ipv4/tcp_l3mdev_accept') != bind_all:
- _cmd(f'sysctl -wq net.ipv4.tcp_l3mdev_accept={bind_all}')
- _cmd(f'sysctl -wq net.ipv4.udp_l3mdev_accept={bind_all}')
-
- for vrf in vrf_config['vrf_remove']:
- name = vrf['name']
- if os.path.isdir(f'/sys/class/net/{name}'):
- _cmd(f'ip -4 route del vrf {name} unreachable default metric 4278198272')
- _cmd(f'ip -6 route del vrf {name} unreachable default metric 4278198272')
- _cmd(f'ip link delete dev {name}')
-
- for vrf in vrf_config['vrf_add']:
- name = vrf['name']
- table = vrf['table']
-
- if not os.path.isdir(f'/sys/class/net/{name}'):
- # For each VRF apart from your default context create a VRF
- # interface with a separate routing table
- _cmd(f'ip link add {name} type vrf table {table}')
- # Start VRf
- _cmd(f'ip link set dev {name} up')
- # The kernel Documentation/networking/vrf.txt also recommends
- # adding unreachable routes to the VRF routing tables so that routes
- # afterwards are taken.
- _cmd(f'ip -4 route add vrf {name} unreachable default metric 4278198272')
- _cmd(f'ip -6 route add vrf {name} unreachable default metric 4278198272')
- # We also should add proper loopback IP addresses to the newly
- # created VRFs for services bound to the loopback address (SNMP, NTP)
- _cmd(f'ip -4 addr add 127.0.0.1/8 dev {name}')
- _cmd(f'ip -6 addr add ::1/128 dev {name}')
-
- # set VRF description for e.g. SNMP monitoring
- Interface(name).set_alias(vrf['description'])
+ bind_all = '0'
+ if 'bind_to_all' in vrf:
+ bind_all = '1'
+ _cmd(f'sysctl -wq net.ipv4.tcp_l3mdev_accept={bind_all}')
+ _cmd(f'sysctl -wq net.ipv4.udp_l3mdev_accept={bind_all}')
+
+ for tmp in (dict_search('vrf_remove', vrf) or []):
+ if os.path.isdir(f'/sys/class/net/{tmp}'):
+ _cmd(f'ip -4 route del vrf {tmp} unreachable default metric 4278198272')
+ _cmd(f'ip -6 route del vrf {tmp} unreachable default metric 4278198272')
+ _cmd(f'ip link delete dev {tmp}')
+
+ if 'name' in vrf:
+ for name, config in vrf['name'].items():
+ table = config['table']
+
+ if not os.path.isdir(f'/sys/class/net/{name}'):
+ # For each VRF apart from your default context create a VRF
+ # interface with a separate routing table
+ _cmd(f'ip link add {name} type vrf table {table}')
+ # The kernel Documentation/networking/vrf.txt also recommends
+ # adding unreachable routes to the VRF routing tables so that routes
+ # afterwards are taken.
+ _cmd(f'ip -4 route add vrf {name} unreachable default metric 4278198272')
+ _cmd(f'ip -6 route add vrf {name} unreachable default metric 4278198272')
+ # We also should add proper loopback IP addresses to the newly
+ # created VRFs for services bound to the loopback address (SNMP, NTP)
+ _cmd(f'ip -4 addr add 127.0.0.1/8 dev {name}')
+ _cmd(f'ip -6 addr add ::1/128 dev {name}')
+
+ # set VRF description for e.g. SNMP monitoring
+ vrf_if = Interface(name)
+ vrf_if.set_alias(config.get('description', ''))
+ # 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
+ # often as 'down'. This is required by some interface implementations
+ # as certain parameters can only be changed when the interface is
+ # in admin-down state. This ensures the link does not flap during
+ # reconfiguration.
+ state = 'down' if 'disable' in config else 'up'
+ vrf_if.set_admin_state(state)
# Linux routing uses rules to find tables - routing targets are then
# looked up in those tables. If the lookup got a matching route, the
@@ -248,13 +197,13 @@ def apply(vrf_config):
local_pref = [r.get('priority') for r in list_rules() if r.get('table') == 'local'][0]
# change preference when VRFs are enabled and local lookup table is default
- if not local_pref and vrf_config['vrf_add']:
+ if not local_pref and 'name' in vrf:
for af in ['-4', '-6']:
_cmd(f'ip {af} rule add pref 32765 table local')
_cmd(f'ip {af} rule del pref 0')
# return to default lookup preference when no VRF is configured
- if not vrf_config['vrf_add']:
+ if 'name' not in vrf:
for af in ['-4', '-6']:
_cmd(f'ip {af} rule add pref 0 table local')
_cmd(f'ip {af} rule del pref 32765')
diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf
new file mode 100644
index 000000000..8265e12dc
--- /dev/null
+++ b/src/etc/sysctl.d/30-vyos-router.conf
@@ -0,0 +1,98 @@
+#
+# VyOS specific sysctl settings, see sysctl.conf (5) for information.
+#
+
+# Panic on OOPS
+kernel.panic_on_oops=1
+
+# Timeout before rebooting on panic
+kernel.panic=60
+
+# Send all core files to /var/core/core.program.pid.time
+kernel.core_pattern=/var/core/core-%e-%p-%t
+
+# ARP configuration
+# arp_filter - allow multiple network interfaces on same subnet
+# arp_announce - avoid local addresses no on target's subnet
+# arp_ignore - reply only if target IP is local_address on the interface
+
+# arp_filter defaults to 1 so set all to 0 so vrrp interfaces can override it.
+net.ipv4.conf.all.arp_filter=0
+
+# https://phabricator.vyos.net/T300
+net.ipv4.conf.all.arp_ignore=0
+
+net.ipv4.conf.all.arp_announce=2
+
+# Enable packet forwarding for IPv4
+net.ipv4.ip_forward=1
+
+# if a primary address is removed from an interface promote the
+# secondary address if available
+net.ipv4.conf.all.promote_secondaries=1
+
+# Ignore ICMP broadcasts sent to broadcast/multicast
+net.ipv4.icmp_echo_ignore_broadcasts=1
+
+# Ignore bogus ICMP errors
+net.ipv4.icmp_ignore_bogus_error_responses=1
+
+# Send ICMP responses with primary address of exiting interface
+net.ipv4.icmp_errors_use_inbound_ifaddr=1
+
+# Log packets with impossible addresses to kernel log
+net.ipv4.conf.all.log_martians=1
+
+# Do not ignore all ICMP ECHO requests by default
+net.ipv4.icmp_echo_ignore_all=0
+
+# Disable source validation by default
+net.ipv4.conf.all.rp_filter=0
+net.ipv4.conf.default.rp_filter=0
+
+# Enable tcp syn-cookies by default
+net.ipv4.tcp_syncookies=1
+
+# Disable accept_redirects by default for any interface
+net.ipv4.conf.all.accept_redirects=0
+net.ipv4.conf.default.accept_redirects=0
+net.ipv6.conf.all.accept_redirects=0
+net.ipv6.conf.default.accept_redirects=0
+
+# Disable accept_source_route by default
+net.ipv4.conf.all.accept_source_route=0
+net.ipv4.conf.default.accept_source_route=0
+net.ipv6.conf.all.accept_source_route=0
+net.ipv6.conf.default.accept_source_route=0
+
+# Enable send_redirects by default
+net.ipv4.conf.all.send_redirects=1
+net.ipv4.conf.default.send_redirects=1
+
+# Increase size of buffer for netlink
+net.core.rmem_max=2097152
+
+# Enable packet forwarding for IPv6
+net.ipv6.conf.all.forwarding=1
+
+# Increase route table limit
+net.ipv6.route.max_size = 262144
+
+# Do not forget IPv6 addresses when a link goes down
+net.ipv6.conf.default.keep_addr_on_down=1
+net.ipv6.conf.all.keep_addr_on_down=1
+
+# Default value of 20 seems to interfere with larger OSPF and VRRP setups
+net.ipv4.igmp_max_memberships = 512
+
+# Enable conntrack helper by default
+net.netfilter.nf_conntrack_helper=1
+
+# Increase default garbage collection thresholds
+net.ipv4.neigh.default.gc_thresh1 = 1024
+net.ipv4.neigh.default.gc_thresh2 = 4096
+net.ipv4.neigh.default.gc_thresh3 = 8192
+#
+net.ipv6.neigh.default.gc_thresh1 = 1024
+net.ipv6.neigh.default.gc_thresh2 = 4096
+net.ipv6.neigh.default.gc_thresh3 = 8192
diff --git a/src/etc/udev/rules.d/42-qemu-usb.rules b/src/etc/udev/rules.d/42-qemu-usb.rules
new file mode 100644
index 000000000..a79543df7
--- /dev/null
+++ b/src/etc/udev/rules.d/42-qemu-usb.rules
@@ -0,0 +1,14 @@
+#
+# Enable autosuspend for qemu emulated usb hid devices.
+#
+# Note that there are buggy qemu versions which advertise remote
+# wakeup support but don't actually implement it correctly. This
+# is the reason why we need a match for the serial number here.
+# The serial number "42" is used to tag the implementations where
+# remote wakeup is working.
+#
+# Gerd Hoffmann <kraxel@xxxxxxxxxx>
+
+ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Mouse", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto"
+ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Tablet", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto"
+ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Keyboard", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto"
diff --git a/src/etc/udev/rules.d/63-hyperv-vf-net.rules b/src/etc/udev/rules.d/63-hyperv-vf-net.rules
new file mode 100644
index 000000000..b4dcb5a39
--- /dev/null
+++ b/src/etc/udev/rules.d/63-hyperv-vf-net.rules
@@ -0,0 +1,5 @@
+ATTR{[dmi/id]sys_vendor}!="Microsoft Corporation", GOTO="end_hyperv_nic"
+
+ACTION=="add", SUBSYSTEM=="net", DRIVERS=="hv_pci", NAME="vf_%k"
+
+LABEL="end_hyperv_nic"
diff --git a/src/etc/udev/rules.d/64-vyos-vmware-net.rules b/src/etc/udev/rules.d/64-vyos-vmware-net.rules
new file mode 100644
index 000000000..66a4a069b
--- /dev/null
+++ b/src/etc/udev/rules.d/64-vyos-vmware-net.rules
@@ -0,0 +1,14 @@
+ATTR{[dmi/id]sys_vendor}!="VMware, Inc.", GOTO="end_vmware_nic"
+
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet0", ENV{VYOS_IFNAME}="eth0"
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet1", ENV{VYOS_IFNAME}="eth1"
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet2", ENV{VYOS_IFNAME}="eth2"
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet3", ENV{VYOS_IFNAME}="eth3"
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet4", ENV{VYOS_IFNAME}="eth4"
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet5", ENV{VYOS_IFNAME}="eth5"
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet6", ENV{VYOS_IFNAME}="eth6"
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet7", ENV{VYOS_IFNAME}="eth7"
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet8", ENV{VYOS_IFNAME}="eth8"
+ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet9", ENV{VYOS_IFNAME}="eth9"
+
+LABEL="end_vmware_nic"
diff --git a/src/etc/udev/rules.d/65-vyatta-net.rules b/src/etc/udev/rules.d/65-vyatta-net.rules
new file mode 100644
index 000000000..2b48c1213
--- /dev/null
+++ b/src/etc/udev/rules.d/65-vyatta-net.rules
@@ -0,0 +1,26 @@
+# These rules use vyatta_net_name to persistently name network interfaces
+# per "hwid" association in the Vyatta configuration file.
+
+ACTION!="add", GOTO="vyatta_net_end"
+SUBSYSTEM!="net", GOTO="vyatta_net_end"
+
+# ignore the interface if a name has already been set
+NAME=="?*", GOTO="vyatta_net_end"
+
+# Do name change for ethernet and wireless devices only
+KERNEL!="eth*|wlan*", GOTO="vyatta_net_end"
+
+# ignore "secondary" monitor interfaces of mac80211 drivers
+KERNEL=="wlan*", ATTRS{type}=="803", GOTO="vyatta_net_end"
+
+# If using VyOS predefined names
+ENV{VYOS_IFNAME}!="eth*", GOTO="end_vyos_predef_names"
+
+DRIVERS=="?*", PROGRAM="vyatta_net_name %k $attr{address} $env{VYOS_IFNAME}", NAME="%c", GOTO="vyatta_net_end"
+
+LABEL="end_vyos_predef_names"
+
+# ignore interfaces without a driver link like bridges and VLANs
+DRIVERS=="?*", PROGRAM="vyatta_net_name %k $attr{address}", NAME="%c"
+
+LABEL="vyatta_net_end"
diff --git a/src/tests/test_template.py b/src/tests/test_template.py
index 544755692..7800d007f 100644
--- a/src/tests/test_template.py
+++ b/src/tests/test_template.py
@@ -93,3 +93,22 @@ class TestVyOSTemplate(TestCase):
self.assertEqual(vyos.template.dec_ip('2001:db8::b/64', '10'), '2001:db8::1')
self.assertEqual(vyos.template.dec_ip('2001:db8::f', '5'), '2001:db8::a')
+ def test_is_network(self):
+ self.assertFalse(vyos.template.is_ip_network('192.0.2.0'))
+ self.assertFalse(vyos.template.is_ip_network('192.0.2.1/24'))
+ self.assertTrue(vyos.template.is_ip_network('192.0.2.0/24'))
+
+ self.assertFalse(vyos.template.is_ip_network('2001:db8::'))
+ self.assertFalse(vyos.template.is_ip_network('2001:db8::ffff'))
+ self.assertTrue(vyos.template.is_ip_network('2001:db8::/48'))
+ self.assertTrue(vyos.template.is_ip_network('2001:db8:1000::/64'))
+
+ def test_is_network(self):
+ self.assertTrue(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/8'))
+ self.assertTrue(vyos.template.compare_netmask('10.0.0.0/16', '20.0.0.0/16'))
+ self.assertFalse(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/16'))
+ self.assertFalse(vyos.template.compare_netmask('10.0.0.1', '20.0.0.0/16'))
+
+ self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/48'))
+ self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/64', '2001:db8:2000::/64'))
+ self.assertFalse(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/64'))
diff --git a/src/validators/allowed-vlan b/src/validators/allowed-vlan
new file mode 100755
index 000000000..11389390b
--- /dev/null
+++ b/src/validators/allowed-vlan
@@ -0,0 +1,19 @@
+#! /usr/bin/python3
+
+import sys
+import re
+
+if __name__ == '__main__':
+ if len(sys.argv)>1:
+ allowed_vlan = sys.argv[1]
+ if re.search('[0-9]{1,4}-[0-9]{1,4}', allowed_vlan):
+ for tmp in allowed_vlan.split('-'):
+ if int(tmp) not in range(1, 4095):
+ sys.exit(1)
+ else:
+ if int(allowed_vlan) not in range(1, 4095):
+ sys.exit(1)
+ else:
+ sys.exit(2)
+
+ sys.exit(0)